Skip to content

Commit

Permalink
Show aliases in data source options for detector and correlation rule…
Browse files Browse the repository at this point in the history
… creation (#864)

* show aliases under data source dropdowns

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>

* add manage rules entrypoint in creation UI

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>

* fixed integ test

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>

---------

Signed-off-by: Amardeepsingh Siglani <amardeep7194@gmail.com>
(cherry picked from commit 95560a8)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
github-actions[bot] committed Feb 7, 2024
1 parent 9a63df0 commit f49c9a3
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 62 deletions.
12 changes: 9 additions & 3 deletions cypress/integration/1_detectors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,15 @@ const validatePendingFieldMappingsPanel = (mappings) => {
});
};

const fillDetailsForm = (detectorName, dataSource) => {
const fillDetailsForm = (detectorName, dataSource, isCustomDataSource = false) => {
getNameField().type(detectorName);
getDataSourceField().selectComboboxItem(dataSource);
if (isCustomDataSource) {
getDataSourceField()
.focus()
.type(dataSource + '{enter}');
} else {
getDataSourceField().selectComboboxItem(dataSource);
}
getDataSourceField().focus().blur();
getLogTypeField().selectComboboxItem(getLogTypeLabel(cypressLogTypeDns));
getLogTypeField().focus().blur();
Expand All @@ -129,7 +135,7 @@ const fillDetailsForm = (detectorName, dataSource) => {
const createDetector = (detectorName, dataSource, expectFailure) => {
getCreateDetectorButton().click({ force: true });

fillDetailsForm(detectorName, dataSource);
fillDetailsForm(detectorName, dataSource, expectFailure);

cy.getElementByText('.euiAccordion .euiTitle', 'Selected detection rules (14)')
.click({ force: true, timeout: 5000 })
Expand Down
35 changes: 14 additions & 21 deletions public/pages/Correlations/containers/CreateCorrelationRule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { CoreServicesContext } from '../../../components/core_services';
import { RouteComponentProps, useParams } from 'react-router-dom';
import { validateName } from '../../../utils/validation';
import { FieldMappingService, IndexService } from '../../../services';
import { errorNotificationToast, getLogTypeOptions } from '../../../utils/helpers';
import { errorNotificationToast, getDataSources, getLogTypeOptions } from '../../../utils/helpers';

export interface CreateCorrelationRuleProps {
indexService: IndexService;
Expand All @@ -51,18 +51,20 @@ export interface CreateCorrelationRuleProps {
notifications: NotificationsStart | null;
}

export interface CorrelationOptions {
export interface CorrelationOption {
label: string;
value: string;
value?: string;
index?: string;
options?: CorrelationOption[];
}

export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
props: CreateCorrelationRuleProps
) => {
const correlationStore = DataStore.correlations;
const [indices, setIndices] = useState<CorrelationOptions[]>([]);
const [indices, setIndices] = useState<CorrelationOption[]>([]);
const [logFieldsByIndex, setLogFieldsByIndex] = useState<{
[index: string]: CorrelationOptions[];
[index: string]: CorrelationOption[];
}>({});
const validateCorrelationRule = useCallback((rule: CorrelationRuleModel) => {
if (!rule.name) {
Expand Down Expand Up @@ -153,26 +155,14 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
};

const context = useContext(CoreServicesContext);
const parseOptions = (indices: string[]) => {
return indices.map(
(index: string): CorrelationOptions => ({
label: index,
value: index,
})
);
};

const getIndices = useCallback(async () => {
try {
const indicesResponse = await props.indexService.getIndices();
if (indicesResponse.ok) {
const indicesNames = parseOptions(
indicesResponse.response.indices.map((index) => index.index)
);
setIndices(indicesNames);
const dataSourcesRes = await getDataSources(props.indexService, props.notifications);
if (dataSourcesRes.ok) {
setIndices(dataSourcesRes.dataSources);
}
} catch (error: any) {}
}, [props.indexService.getIndices]);
}, [props.indexService, props.notifications]);

useEffect(() => {
getIndices();
Expand Down Expand Up @@ -286,6 +276,9 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
);
updateLogFieldsForIndex(e[0]?.value || '');
}}
renderOption={(option: CorrelationOption) => {
return option.index ? `${option.label} (${option.index})` : option.label;
}}
onBlur={props.handleBlur(`queries[${queryIdx}].index`)}
selectedOptions={
query.index ? [{ value: query.index, label: query.index }] : []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ export const DetectionRules: React.FC<DetectionRulesProps> = ({
</EuiText>
</div>
}
extraAction={
<EuiButton href={`#${ROUTES.RULES}`} target="_blank">
Manage <EuiIcon type={'popout'} />
</EuiButton>
}
id={'detectorRulesAccordion'}
initialIsOpen={false}
isLoading={loading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { IndexOption } from '../../../../../Detectors/models/interfaces';
import { MIN_NUM_DATA_SOURCES } from '../../../../../Detectors/utils/constants';
import IndexService from '../../../../../../services/IndexService';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { errorNotificationToast } from '../../../../../../utils/helpers';
import { getDataSources } from '../../../../../../utils/helpers';
import _ from 'lodash';
import { FieldMappingService } from '../../../../../../services';

Expand Down Expand Up @@ -57,41 +57,28 @@ export default class DetectorDataSource extends Component<
}

componentDidMount = async () => {
this.getIndices();
this.getDataSources();
};

getIndices = async () => {
getDataSources = async () => {
this.setState({ loading: true });
try {
const indicesResponse = await this.props.indexService.getIndices();
if (indicesResponse.ok) {
const indices = indicesResponse.response.indices;
const indicesNames = indices.map((index) => index.index);

this.setState({
loading: false,
indexOptions: this.parseOptions(indicesNames),
});
} else {
errorNotificationToast(
this.props.notifications,
'retrieve',
'indices',
indicesResponse.error
);
this.setState({ errorMessage: indicesResponse.error });
}
} catch (error: any) {
errorNotificationToast(this.props.notifications, 'retrieve', 'indices', error);
const res = await getDataSources(this.props.indexService, this.props.notifications);

if (res.ok) {
this.setState({
loading: false,
indexOptions: res.dataSources,
});
} else {
this.setState({ loading: false, errorMessage: res.error });
}
this.setState({ loading: false });
};

parseOptions = (indices: string[]) => {
return indices.map((index) => ({ label: index }));
parseOptions = (options: string[]) => {
return options.map((option) => ({ label: option }));
};

onCreateOption = (searchValue: string, options: EuiComboBoxOptionOption[]) => {
onCreateOption = (searchValue: string) => {
const parsedOptions = this.parseOptions(this.props.detectorIndices);
parsedOptions.push({ label: searchValue });
this.onSelectionChange(parsedOptions);
Expand Down Expand Up @@ -174,6 +161,9 @@ export default class DetectorDataSource extends Component<
isInvalid={!!errorMessage}
isClearable={true}
data-test-subj={'define-detector-select-data-source'}
renderOption={(option: IndexOption) => {
return option.index ? `${option.label} (${option.index})` : option.label;
}}
/>
</EuiFormRow>
{differentLogTypesDetected ? (
Expand Down
1 change: 1 addition & 0 deletions public/pages/Detectors/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

export interface IndexOption {
label: string;
index?: string;
}
9 changes: 8 additions & 1 deletion public/services/IndexService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { HttpSetup } from 'opensearch-dashboards/public';
import { ServerResponse } from '../../server/models/types';
import { GetIndicesResponse } from '../../server/models/interfaces';
import { GetAliasesResponse, GetIndicesResponse } from '../../server/models/interfaces';
import { API } from '../../server/utils/constants';
import { IIndexService } from '../../types';

Expand All @@ -32,6 +32,13 @@ export default class IndexService implements IIndexService {
return response;
};

getAliases = async (): Promise<ServerResponse<GetAliasesResponse>> => {
const url = `..${API.ALIASES_BASE}`;
const response = (await this.httpClient.get(url)) as ServerResponse<GetAliasesResponse>;

return response;
};

updateAliases = async (actions: any): Promise<ServerResponse<{}>> => {
const url = `..${API.UPDATE_ALIASES}`;
const response = (await this.httpClient.post(url, {
Expand Down
66 changes: 65 additions & 1 deletion public/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { parse, View } from 'vega/build-es5/vega.js';
import { expressionInterpreter as vegaExpressionInterpreter } from 'vega-interpreter/build/vega-interpreter';
import { RuleInfo } from '../../server/models/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { OpenSearchService } from '../services';
import { IndexService, OpenSearchService } from '../services';
import { ruleSeverity, ruleTypes } from '../pages/Rules/utils/constants';
import { Handler } from 'vega-tooltip';
import _ from 'lodash';
Expand Down Expand Up @@ -432,3 +432,67 @@ export function addDetectionType(
export function isThreatIntelQuery(queryId: string) {
return queryId?.startsWith('threat_intel_');
}

export async function getDataSources(
indexService: IndexService,
notifications: any
): Promise<
| {
ok: true;
dataSources: { label: string; options: { label: string; value: string; index?: string }[] }[];
}
| { ok: false; error: string }
> {
const dataSourceOptions = [];
try {
const aliasesResponse = await indexService.getAliases();
const indicesResponse = await indexService.getIndices();

if (aliasesResponse.ok) {
const aliases = aliasesResponse.response.aliases.filter(
({ index }) => !index.startsWith('.')
);
const aliasOptions = aliases.map(({ alias, index }) => ({
label: alias,
index: index,
value: alias,
}));

dataSourceOptions.push({
label: 'Aliases',
options: aliasOptions,
});
} else {
errorNotificationToast(notifications, 'retrieve', 'aliases', aliasesResponse.error);
return { ok: false, error: aliasesResponse.error };
}

if (indicesResponse.ok) {
const indices = indicesResponse.response.indices;
const indexOptions = indices
.map(({ index }) => ({ label: index, value: index }))
.filter(({ label }) => !label.startsWith('.'));

dataSourceOptions.push({
label: 'Indices',
options: indexOptions,
});
} else {
errorNotificationToast(notifications, 'retrieve', 'indices', indicesResponse.error);

return { ok: false, error: indicesResponse.error };
}

return {
ok: true,
dataSources: dataSourceOptions,
};
} catch (error: any) {
errorNotificationToast(notifications, 'retrieve', 'indices', error);

return {
ok: false,
error,
};
}
}
10 changes: 10 additions & 0 deletions server/models/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface SecurityAnalyticsApi {
readonly CORRELATION_BASE: string;
readonly SEARCH_DETECTORS: string;
readonly INDICES_BASE: string;
readonly ALIASES_BASE: string;
readonly FINDINGS_BASE: string;
readonly GET_FINDINGS: string;
readonly DOCUMENT_IDS_QUERY: string;
Expand Down Expand Up @@ -57,6 +58,10 @@ export interface GetIndicesResponse {
indices: CatIndex[];
}

export interface GetAliasesResponse {
aliases: CatAlias[];
}

// Default _cat index response
export interface CatIndex {
'docs.count': string;
Expand All @@ -72,6 +77,11 @@ export interface CatIndex {
data_stream: string | null;
}

export interface CatAlias {
alias: string;
index: string;
}

export interface SearchResponse<T> {
hits: {
total: { value: number };
Expand Down
8 changes: 8 additions & 0 deletions server/routes/IndexRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export function setupIndexRoutes(services: NodeServices, router: IRouter) {
indexService.getIndices
);

router.get(
{
path: API.ALIASES_BASE,
validate: {},
},
indexService.getAliases
);

router.post(
{
path: `${API.INDICES_BASE}`,
Expand Down
Loading

0 comments on commit f49c9a3

Please sign in to comment.