From 9bf0c69ab90c2bce1e7f01ed89d48aab1ecac7a3 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Fri, 13 Jan 2023 21:47:14 +0100 Subject: [PATCH 01/14] create index pattern modal Signed-off-by: Aleksandar Djindjic --- public/models/interfaces.ts | 9 ++ .../components/CreateIndexPatternForm.tsx | 146 ++++++++++++++++++ .../components/FindingDetailsFlyout.tsx | 61 +++++++- public/services/OpenSearchService.ts | 25 +++ 4 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 public/pages/Findings/components/CreateIndexPatternForm.tsx diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 767423ad8..952101bdd 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -32,6 +32,15 @@ export interface RuleOptions { tags: string[]; } +export interface GetFieldsOptions { + pattern?: string; + type?: string; + params?: any; + lookBack?: boolean; + metaFields?: string[]; + dataSourceId?: string; +} + export interface RulesSharedState { page: RulesPage; rulesOptions: RuleOptions[]; diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx new file mode 100644 index 000000000..9a611fcd0 --- /dev/null +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -0,0 +1,146 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useEffect, useState } from 'react'; +import { Formik, Form, FormikErrors } from 'formik'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldText, + EuiButton, + EuiSpacer, + EuiComboBox, + EuiText, +} from '@elastic/eui'; +import { OpenSearchService } from '../../../services'; + +export interface CreateIndexPatternFormModel { + name: string; + timeField: string; +} + +export interface CreateIndexPatternFormProps { + initialValue: { + name: string; + }; + submit: (values: CreateIndexPatternFormModel) => void; + cancel: () => void; + opensearchService: OpenSearchService; +} + +export const CreateIndexPatternForm: React.FC = ({ + initialValue, + submit, + cancel, + opensearchService, +}) => { + const [timeFileds, setTimeFields] = useState([]); + + const getTimeFields = async () => { + opensearchService + .getFieldsForWildcard({ + pattern: 'cyp*', + metaFields: ['_source', '_id', '_type', '_index', '_score'], + params: {}, + }) + .then(({ fields }) => { + console.log(fields); + setTimeFields(fields.map((f) => f.name)); + }); + }; + + useEffect(() => { + getTimeFields(); + }, []); + + return ( + { + const errors: FormikErrors = {}; + + if (!values.name) { + errors.name = 'Index patter name is required'; + } + + if (!values.timeField) { + errors.timeField = 'Time field is required'; + } + + return errors; + }} + onSubmit={(values, { setSubmitting }) => { + setSubmitting(false); + submit(values); + }} + > + {(props) => ( +
+ + Specify index pattern name + + } + isInvalid={props.touched.name && !!props.errors?.name} + error={props.errors.name} + > + { + props.handleChange('name')(e); + }} + onBlur={props.handleBlur('name')} + value={props.values.name} + /> + + + + Time filed + + } + isInvalid={props.touched.timeField && !!props.errors?.timeField} + error={props.errors.timeField} + > + ({ value: field, label: field }))} + singleSelection={{ asPlainText: true }} + onChange={(e) => { + props.handleChange('timeField')(e[0]?.value ? e[0].value : ''); + }} + onBlur={props.handleBlur('timeField')} + selectedOptions={ + props.values.timeField + ? [{ value: props.values.timeField, label: props.values.timeField }] + : [] + } + /> + + + + + + + Cancel + + + props.handleSubmit()}> + Create index pattern + + + + + )} +
+ ); +}; diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index d7d6c2d56..204d82a50 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -19,6 +19,10 @@ import { EuiFormRow, EuiHorizontalRule, EuiLink, + EuiModal, + EuiModalBody, + EuiModalHeader, + EuiModalHeaderTitle, EuiSpacer, EuiText, EuiTitle, @@ -31,6 +35,7 @@ import { RuleSource } from '../../../../server/models/interfaces'; import { RuleItemInfoBase } from '../../Rules/models/types'; import { OpenSearchService } from '../../../services'; import { RuleTableItem } from '../../Rules/utils/helpers'; +import { CreateIndexPatternForm } from './CreateIndexPatternForm'; interface FindingDetailsFlyoutProps { finding: Finding; @@ -44,6 +49,7 @@ interface FindingDetailsFlyoutState { loading: boolean; ruleViewerFlyoutData: RuleTableItem | null; indexPatternId?: string; + isCreateIndexPatternModalVisible: boolean; } export default class FindingDetailsFlyout extends Component< @@ -55,6 +61,7 @@ export default class FindingDetailsFlyout extends Component< this.state = { loading: false, ruleViewerFlyoutData: null, + isCreateIndexPatternModalVisible: false, }; } @@ -217,12 +224,16 @@ export default class FindingDetailsFlyout extends Component< { + if (indexPatternId) { + window.open( + `discover#/context/${indexPatternId}/${related_doc_ids[0]}`, + '_blank' + ); + } else { + this.setState({ ...this.state, isCreateIndexPatternModalVisible: true }); + } + }} > View surrounding documents @@ -266,6 +277,42 @@ export default class FindingDetailsFlyout extends Component< ); } + createIndexPatternModal() { + if (this.state.isCreateIndexPatternModalVisible) { + return ( + this.setState({ ...this.state, isCreateIndexPatternModalVisible: false })} + > + + +

Create index pattern to view documents

+
+
+ + + + An index pattern is required to view all surrounding documents within the index. + Create an index pattern to continue. + + + + this.setState({ ...this.state, isCreateIndexPatternModalVisible: false }) + } + submit={console.log} + > + +
+ ); + } + } + render() { const { finding: { @@ -294,7 +341,7 @@ export default class FindingDetailsFlyout extends Component< ruleTableItem={this.state.ruleViewerFlyoutData} /> )} - + {this.createIndexPatternModal()} diff --git a/public/services/OpenSearchService.ts b/public/services/OpenSearchService.ts index f1b1c6632..c88047b05 100644 --- a/public/services/OpenSearchService.ts +++ b/public/services/OpenSearchService.ts @@ -11,6 +11,7 @@ import { import { ServerResponse } from '../../server/models/types'; import { SearchResponse, Plugin } from '../../server/models/interfaces'; import { API } from '../../server/utils/constants'; +import { GetFieldsOptions } from '../models/interfaces'; export default class OpenSearchService { constructor( @@ -56,4 +57,28 @@ export default class OpenSearchService { return Promise.resolve(indexPatterns); }; + + getFieldsForWildcard(options: GetFieldsOptions = {}) { + const { pattern, metaFields, type, params, dataSourceId } = options; + + let query; + + if (type) { + query = { + pattern, + meta_fields: metaFields, + params: JSON.stringify(params), + data_source: dataSourceId, + }; + } else { + query = { + pattern, + meta_fields: metaFields, + data_source: dataSourceId, + }; + } + + let url = `../api/index_patterns/_fields_for_wildcard`; + return this.httpClient.get(url, { query }); + } } From 1e4fb7026f08da42f0415f78cb69fc86f3fa2c92 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Mon, 16 Jan 2023 18:20:54 +0100 Subject: [PATCH 02/14] validate index pattern name and fill timeFields Signed-off-by: Aleksandar Djindjic --- .../components/CreateIndexPatternForm.tsx | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 9a611fcd0..da130c5ca 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -17,6 +17,12 @@ import { } from '@elastic/eui'; import { OpenSearchService } from '../../../services'; +const ILLEGAL_CHARACTERS = [' ', '\\', '/', '?', '"', '<', '>', '|']; + +const containsIllegalCharacters = (pattern: string) => { + return ILLEGAL_CHARACTERS.some((char) => pattern.includes(char)); +}; + export interface CreateIndexPatternFormModel { name: string; timeField: string; @@ -39,22 +45,26 @@ export const CreateIndexPatternForm: React.FC = ({ }) => { const [timeFileds, setTimeFields] = useState([]); - const getTimeFields = async () => { - opensearchService + const getTimeFields = async (name: string) => { + return opensearchService .getFieldsForWildcard({ - pattern: 'cyp*', + pattern: `${name}`, metaFields: ['_source', '_id', '_type', '_index', '_score'], params: {}, }) - .then(({ fields }) => { - console.log(fields); - setTimeFields(fields.map((f) => f.name)); + .then((res) => { + return res.fields.filter((f) => f.type === 'date').map((f) => f.name); + }) + .catch(() => { + return []; }); }; useEffect(() => { - getTimeFields(); - }, []); + getTimeFields(initialValue.name).then((fields) => { + setTimeFields(fields); + }); + }, [initialValue.name]); return ( = ({ errors.timeField = 'Time field is required'; } + if (containsIllegalCharacters(values.name)) { + errors.name = + 'A index pattern cannot contain spaces or the characters: , /, ?, ", <, >, |'; + } + return errors; }} onSubmit={(values, { setSubmitting }) => { @@ -92,8 +107,11 @@ export const CreateIndexPatternForm: React.FC = ({ isInvalid={props.touched.name && !!props.errors.name} placeholder="Enter index pattern name" data-test-subj={'index_pattern_name_field'} - onChange={(e) => { + onChange={async (e) => { props.handleChange('name')(e); + const fileds = await getTimeFields(e.target.value); + setTimeFields(fileds); + props.setFieldValue('timeField', ''); }} onBlur={props.handleBlur('name')} value={props.values.name} From feeab62d866f141a09f31df89c9651b109dba2bd Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 17 Jan 2023 16:11:37 +0100 Subject: [PATCH 03/14] reuse SavedObjectsService from dashboards core Signed-off-by: Aleksandar Djindjic --- public/models/interfaces.ts | 2 ++ .../components/CreateIndexPatternForm.tsx | 7 +++-- .../components/FindingDetailsFlyout.tsx | 4 ++- .../FindingsTable/FindingsTable.tsx | 3 +- .../Findings/containers/Findings/Findings.tsx | 2 ++ public/pages/Main/Main.tsx | 1 + public/security_analytics_app.tsx | 9 +++++- public/services/SavedObjectsService.ts | 28 +++++++++++++++++++ public/services/index.ts | 2 ++ 9 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 public/services/SavedObjectsService.ts diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 952101bdd..313997006 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -12,6 +12,7 @@ import { IndexService, RuleService, NotificationsService, + SavedObjectsService, } from '../services'; export interface BrowserServices { @@ -23,6 +24,7 @@ export interface BrowserServices { alertService: AlertsService; ruleService: RuleService; notificationsService: NotificationsService; + savedObjectsService: SavedObjectsService; } export interface RuleOptions { diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index da130c5ca..9c18e9a15 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -15,7 +15,7 @@ import { EuiComboBox, EuiText, } from '@elastic/eui'; -import { OpenSearchService } from '../../../services'; +import { OpenSearchService, SavedObjectsService } from '../../../services'; const ILLEGAL_CHARACTERS = [' ', '\\', '/', '?', '"', '<', '>', '|']; @@ -35,6 +35,7 @@ export interface CreateIndexPatternFormProps { submit: (values: CreateIndexPatternFormModel) => void; cancel: () => void; opensearchService: OpenSearchService; + savedObjectsService: SavedObjectsService; } export const CreateIndexPatternForm: React.FC = ({ @@ -42,6 +43,7 @@ export const CreateIndexPatternForm: React.FC = ({ submit, cancel, opensearchService, + savedObjectsService, }) => { const [timeFileds, setTimeFields] = useState([]); @@ -87,8 +89,9 @@ export const CreateIndexPatternForm: React.FC = ({ return errors; }} - onSubmit={(values, { setSubmitting }) => { + onSubmit={async (values, { setSubmitting }) => { setSubmitting(false); + await savedObjectsService.createIndexPattern(); submit(values); }} > diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index 204d82a50..93cb0482d 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -33,7 +33,7 @@ import { Finding, Query } from '../models/interfaces'; import { RuleViewerFlyout } from '../../Rules/components/RuleViewerFlyout/RuleViewerFlyout'; import { RuleSource } from '../../../../server/models/interfaces'; import { RuleItemInfoBase } from '../../Rules/models/types'; -import { OpenSearchService } from '../../../services'; +import { OpenSearchService, SavedObjectsService } from '../../../services'; import { RuleTableItem } from '../../Rules/utils/helpers'; import { CreateIndexPatternForm } from './CreateIndexPatternForm'; @@ -42,6 +42,7 @@ interface FindingDetailsFlyoutProps { backButton?: React.ReactNode; allRules: { [id: string]: RuleSource }; opensearchService: OpenSearchService; + savedObjectsService: SavedObjectsService; closeFlyout: () => void; } @@ -298,6 +299,7 @@ export default class FindingDetailsFlyout extends Component< void; onFindingsFiltered: (findings: FindingItemType[]) => void; hasNotificationsPlugin: boolean; + savedObjectsService: SavedObjectsService; } interface FindingsTableState { diff --git a/public/pages/Findings/containers/Findings/Findings.tsx b/public/pages/Findings/containers/Findings/Findings.tsx index dfeab74b0..0141ed6d9 100644 --- a/public/pages/Findings/containers/Findings/Findings.tsx +++ b/public/pages/Findings/containers/Findings/Findings.tsx @@ -22,6 +22,7 @@ import { NotificationsService, OpenSearchService, RuleService, + SavedObjectsService, } from '../../../../services'; import { BREADCRUMBS, @@ -59,6 +60,7 @@ interface FindingsProps extends RouteComponentProps { detectorService: DetectorsService; findingsService: FindingsService; notificationsService: NotificationsService; + savedObjectsService: SavedObjectsService; opensearchService: OpenSearchService; ruleService: RuleService; notifications: NotificationsStart; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 933955615..ef0026e13 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -239,6 +239,7 @@ export default class Main extends Component { detectorService={services.detectorsService} ruleService={services.ruleService} notificationsService={services.notificationsService} + savedObjectsService={services.savedObjectsService} notifications={core?.notifications} /> )} diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index 73caff669..81ed30e26 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -7,7 +7,12 @@ import { CoreStart, AppMountParameters } from 'opensearch-dashboards/public'; import React from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Route } from 'react-router-dom'; -import { AlertsService, NotificationsService, ServicesContext } from './services'; +import { + AlertsService, + NotificationsService, + ServicesContext, + SavedObjectsService, +} from './services'; import { DarkModeContext } from './components/DarkMode'; import Main from './pages/Main'; import { CoreServicesContext } from './components/core_services'; @@ -31,6 +36,7 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters, land const alertsService = new AlertsService(http); const ruleService = new RuleService(http); const notificationsService = new NotificationsService(http); + const savedObjectsService = new SavedObjectsService(savedObjects); const services: BrowserServices = { detectorsService, @@ -41,6 +47,7 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters, land ruleService, alertService: alertsService, notificationsService, + savedObjectsService, }; const isDarkMode = coreStart.uiSettings.get('theme:darkMode') || false; diff --git a/public/services/SavedObjectsService.ts b/public/services/SavedObjectsService.ts new file mode 100644 index 000000000..82019ebf7 --- /dev/null +++ b/public/services/SavedObjectsService.ts @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObjectsStart } from 'opensearch-dashboards/public'; + +export default class SavedObjectsService { + private savedObjectsStart: SavedObjectsStart; + + constructor(savedObjectsStart: SavedObjectsStart) { + this.savedObjectsStart = savedObjectsStart; + } + + createIndexPattern = async (): Promise => { + const attributes = { + title: 'cy*', + timeFieldName: 'EventReceivedTime', + fields: + '[{"count":0,"name":"AccountName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountName"}}},{"count":0,"name":"AccountType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountType"}}},{"count":0,"name":"Category","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Category.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Category"}}},{"count":0,"name":"Channel","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Channel.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Channel"}}},{"count":0,"name":"CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Domain"}}},{"count":0,"name":"EventID","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventReceivedTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"EventType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"EventType"}}},{"count":0,"name":"ExecutionProcessID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ExecutionThreadID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"HostName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Image"}}},{"count":0,"name":"Initiated","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Initiated.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Initiated"}}},{"count":0,"name":"Keywords","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Keywords.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Keywords"}}},{"count":0,"name":"Message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Opcode"}}},{"count":0,"name":"OpcodeValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ProcessGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessGuid"}}},{"count":0,"name":"ProcessId","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessId.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessId"}}},{"count":0,"name":"ProviderGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProviderGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProviderGuid"}}},{"count":0,"name":"Provider_Name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryName"}}},{"count":0,"name":"QueryResults","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryResults.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryResults"}}},{"count":0,"name":"QueryStatus","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryStatus.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryStatus"}}},{"count":0,"name":"RecordNumber","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ServiceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Severity"}}},{"count":0,"name":"SeverityValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"SourceModuleName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleName"}}},{"count":0,"name":"SourceModuleType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleType"}}},{"count":0,"name":"SourceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceName"}}},{"count":0,"name":"TargetObject","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"TargetObject.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"TargetObject"}}},{"count":0,"name":"TaskValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"UserID","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UserID.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UserID"}}},{"count":0,"name":"UtcTime","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UtcTime.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UtcTime"}}},{"count":0,"name":"Version","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"_id","type":"string","esTypes":["_id"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_index","type":"string","esTypes":["_index"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_score","type":"number","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_source","type":"_source","esTypes":["_source"],"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_type","type":"string","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"event_uid","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"windows-event_data-CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-hostname","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-provider-name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-servicename","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false}]', + }; + const options = { + id: '', + references: [], + }; + await this.savedObjectsStart.client.create('index-pattern', attributes, options); + }; +} diff --git a/public/services/index.ts b/public/services/index.ts index 4ef1fd575..600f06862 100644 --- a/public/services/index.ts +++ b/public/services/index.ts @@ -12,6 +12,7 @@ import AlertsService from './AlertsService'; import RuleService from './RuleService'; import IndexService from './IndexService'; import NotificationsService from './NotificationsService'; +import SavedObjectsService from './SavedObjectsService'; export { ServicesConsumer, @@ -24,4 +25,5 @@ export { RuleService, IndexService, NotificationsService, + SavedObjectsService, }; From cc44b413666e3fc088d4e5ea1cd6fa24cbff679f Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 17 Jan 2023 16:24:05 +0100 Subject: [PATCH 04/14] take title and time field from the form Signed-off-by: Aleksandar Djindjic --- public/pages/Findings/components/CreateIndexPatternForm.tsx | 2 +- public/services/SavedObjectsService.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 9c18e9a15..e0a621c4b 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -91,7 +91,7 @@ export const CreateIndexPatternForm: React.FC = ({ }} onSubmit={async (values, { setSubmitting }) => { setSubmitting(false); - await savedObjectsService.createIndexPattern(); + await savedObjectsService.createIndexPattern(values.name, values.timeField); submit(values); }} > diff --git a/public/services/SavedObjectsService.ts b/public/services/SavedObjectsService.ts index 82019ebf7..4715dd8d8 100644 --- a/public/services/SavedObjectsService.ts +++ b/public/services/SavedObjectsService.ts @@ -12,10 +12,10 @@ export default class SavedObjectsService { this.savedObjectsStart = savedObjectsStart; } - createIndexPattern = async (): Promise => { + createIndexPattern = async (title: string, timeFieldName: string): Promise => { const attributes = { - title: 'cy*', - timeFieldName: 'EventReceivedTime', + title, + timeFieldName, fields: '[{"count":0,"name":"AccountName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountName"}}},{"count":0,"name":"AccountType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountType"}}},{"count":0,"name":"Category","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Category.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Category"}}},{"count":0,"name":"Channel","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Channel.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Channel"}}},{"count":0,"name":"CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Domain"}}},{"count":0,"name":"EventID","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventReceivedTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"EventType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"EventType"}}},{"count":0,"name":"ExecutionProcessID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ExecutionThreadID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"HostName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Image"}}},{"count":0,"name":"Initiated","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Initiated.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Initiated"}}},{"count":0,"name":"Keywords","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Keywords.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Keywords"}}},{"count":0,"name":"Message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Opcode"}}},{"count":0,"name":"OpcodeValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ProcessGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessGuid"}}},{"count":0,"name":"ProcessId","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessId.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessId"}}},{"count":0,"name":"ProviderGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProviderGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProviderGuid"}}},{"count":0,"name":"Provider_Name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryName"}}},{"count":0,"name":"QueryResults","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryResults.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryResults"}}},{"count":0,"name":"QueryStatus","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryStatus.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryStatus"}}},{"count":0,"name":"RecordNumber","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ServiceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Severity"}}},{"count":0,"name":"SeverityValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"SourceModuleName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleName"}}},{"count":0,"name":"SourceModuleType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleType"}}},{"count":0,"name":"SourceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceName"}}},{"count":0,"name":"TargetObject","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"TargetObject.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"TargetObject"}}},{"count":0,"name":"TaskValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"UserID","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UserID.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UserID"}}},{"count":0,"name":"UtcTime","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UtcTime.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UtcTime"}}},{"count":0,"name":"Version","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"_id","type":"string","esTypes":["_id"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_index","type":"string","esTypes":["_index"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_score","type":"number","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_source","type":"_source","esTypes":["_source"],"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_type","type":"string","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"event_uid","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"windows-event_data-CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-hostname","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-provider-name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-servicename","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false}]', }; From 31a33dc1da5e33c4e7a5e3c62b054800005128ed Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Wed, 18 Jan 2023 22:31:54 +0100 Subject: [PATCH 05/14] reuse index patterns service from core dashboards Signed-off-by: Aleksandar Djindjic --- opensearch_dashboards.json | 2 +- public/models/interfaces.ts | 4 +-- .../components/CreateIndexPatternForm.tsx | 34 +++++++++++++------ .../components/FindingDetailsFlyout.tsx | 21 ++++++++---- .../FindingsTable/FindingsTable.tsx | 5 +-- .../Findings/containers/Findings/Findings.tsx | 4 +-- public/pages/Main/Main.tsx | 2 +- public/plugin.ts | 31 ++++++++++++++--- public/security_analytics_app.tsx | 16 ++++++--- public/services/IndexPatternsService.ts | 26 ++++++++++++++ public/services/OpenSearchService.ts | 24 ------------- public/services/SavedObjectsService.ts | 28 --------------- public/services/index.ts | 4 +-- 13 files changed, 114 insertions(+), 87 deletions(-) create mode 100644 public/services/IndexPatternsService.ts delete mode 100644 public/services/SavedObjectsService.ts diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 011678ca0..693a49b90 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -3,7 +3,7 @@ "version": "2.4.0.0", "opensearchDashboardsVersion": "2.4.0", "configPath": ["opensearch_security_analytics"], - "requiredPlugins": [], + "requiredPlugins": ["data"], "server": true, "ui": true } diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 313997006..b61c5d11a 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -12,7 +12,7 @@ import { IndexService, RuleService, NotificationsService, - SavedObjectsService, + IndexPatternsService, } from '../services'; export interface BrowserServices { @@ -24,7 +24,7 @@ export interface BrowserServices { alertService: AlertsService; ruleService: RuleService; notificationsService: NotificationsService; - savedObjectsService: SavedObjectsService; + indexPatternsService?: IndexPatternsService; } export interface RuleOptions { diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index e0a621c4b..2c988e470 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -15,7 +15,7 @@ import { EuiComboBox, EuiText, } from '@elastic/eui'; -import { OpenSearchService, SavedObjectsService } from '../../../services'; +import { IndexPatternsService } from '../../../services'; const ILLEGAL_CHARACTERS = [' ', '\\', '/', '?', '"', '<', '>', '|']; @@ -32,30 +32,32 @@ export interface CreateIndexPatternFormProps { initialValue: { name: string; }; - submit: (values: CreateIndexPatternFormModel) => void; + submit: (values: string) => void; cancel: () => void; - opensearchService: OpenSearchService; - savedObjectsService: SavedObjectsService; + indexPatternsService?: IndexPatternsService; } export const CreateIndexPatternForm: React.FC = ({ initialValue, submit, cancel, - opensearchService, - savedObjectsService, + indexPatternsService, }) => { const [timeFileds, setTimeFields] = useState([]); - const getTimeFields = async (name: string) => { - return opensearchService + const getTimeFields = async (name: string): Promise => { + if (!indexPatternsService) { + return []; + } + + return indexPatternsService .getFieldsForWildcard({ pattern: `${name}`, metaFields: ['_source', '_id', '_type', '_index', '_score'], params: {}, }) .then((res) => { - return res.fields.filter((f) => f.type === 'date').map((f) => f.name); + return res.filter((f) => f.type === 'date').map((f) => f.name); }) .catch(() => { return []; @@ -91,8 +93,18 @@ export const CreateIndexPatternForm: React.FC = ({ }} onSubmit={async (values, { setSubmitting }) => { setSubmitting(false); - await savedObjectsService.createIndexPattern(values.name, values.timeField); - submit(values); + if (!indexPatternsService) { + return; + } + try { + const newIndex = await indexPatternsService.createAndSave({ + title: values.name, + timeFieldName: values.timeField, + }); + if (newIndex.id) { + submit(newIndex.id); + } + } catch (e) {} }} > {(props) => ( diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index 93cb0482d..39f48a00c 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -33,7 +33,7 @@ import { Finding, Query } from '../models/interfaces'; import { RuleViewerFlyout } from '../../Rules/components/RuleViewerFlyout/RuleViewerFlyout'; import { RuleSource } from '../../../../server/models/interfaces'; import { RuleItemInfoBase } from '../../Rules/models/types'; -import { OpenSearchService, SavedObjectsService } from '../../../services'; +import { OpenSearchService, IndexPatternsService } from '../../../services'; import { RuleTableItem } from '../../Rules/utils/helpers'; import { CreateIndexPatternForm } from './CreateIndexPatternForm'; @@ -42,7 +42,7 @@ interface FindingDetailsFlyoutProps { backButton?: React.ReactNode; allRules: { [id: string]: RuleSource }; opensearchService: OpenSearchService; - savedObjectsService: SavedObjectsService; + indexPatternsService?: IndexPatternsService; closeFlyout: () => void; } @@ -279,6 +279,9 @@ export default class FindingDetailsFlyout extends Component< } createIndexPatternModal() { + const { + finding: { related_doc_ids }, + } = this.props; if (this.state.isCreateIndexPatternModalVisible) { return ( this.setState({ ...this.state, isCreateIndexPatternModalVisible: false }) } - submit={console.log} + submit={(indexPatternId) => { + console.log(indexPatternId); + this.setState({ + ...this.state, + indexPatternId, + isCreateIndexPatternModalVisible: false, + }); + window.open(`discover#/context/${indexPatternId}/${related_doc_ids[0]}`, '_blank'); + }} > diff --git a/public/pages/Findings/components/FindingsTable/FindingsTable.tsx b/public/pages/Findings/components/FindingsTable/FindingsTable.tsx index 38c2c86d0..475a34a25 100644 --- a/public/pages/Findings/components/FindingsTable/FindingsTable.tsx +++ b/public/pages/Findings/components/FindingsTable/FindingsTable.tsx @@ -17,7 +17,7 @@ import { FieldValueSelectionFilterConfigType } from '@elastic/eui/src/components import dateMath from '@elastic/datemath'; import { capitalizeFirstLetter, renderTime } from '../../../../utils/helpers'; import { DEFAULT_EMPTY_DATA } from '../../../../utils/constants'; -import { DetectorsService, OpenSearchService, SavedObjectsService } from '../../../../services'; +import { DetectorsService, OpenSearchService, IndexPatternsService } from '../../../../services'; import FindingDetailsFlyout from '../FindingDetailsFlyout'; import { Finding } from '../../models/interfaces'; import CreateAlertFlyout from '../CreateAlertFlyout'; @@ -39,7 +39,7 @@ interface FindingsTableProps extends RouteComponentProps { onRefresh: () => void; onFindingsFiltered: (findings: FindingItemType[]) => void; hasNotificationsPlugin: boolean; - savedObjectsService: SavedObjectsService; + indexPatternsService?: IndexPatternsService; } interface FindingsTableState { @@ -101,6 +101,7 @@ export default class FindingsTable extends Component ), flyoutOpen: true, diff --git a/public/pages/Findings/containers/Findings/Findings.tsx b/public/pages/Findings/containers/Findings/Findings.tsx index 0141ed6d9..27afa1814 100644 --- a/public/pages/Findings/containers/Findings/Findings.tsx +++ b/public/pages/Findings/containers/Findings/Findings.tsx @@ -22,7 +22,7 @@ import { NotificationsService, OpenSearchService, RuleService, - SavedObjectsService, + IndexPatternsService, } from '../../../../services'; import { BREADCRUMBS, @@ -60,7 +60,7 @@ interface FindingsProps extends RouteComponentProps { detectorService: DetectorsService; findingsService: FindingsService; notificationsService: NotificationsService; - savedObjectsService: SavedObjectsService; + indexPatternsService?: IndexPatternsService; opensearchService: OpenSearchService; ruleService: RuleService; notifications: NotificationsStart; diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index ef0026e13..7a1d7a28f 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -239,7 +239,7 @@ export default class Main extends Component { detectorService={services.detectorsService} ruleService={services.ruleService} notificationsService={services.notificationsService} - savedObjectsService={services.savedObjectsService} + indexPatternsService={services.indexPatternsService} notifications={core?.notifications} /> )} diff --git a/public/plugin.ts b/public/plugin.ts index e332ab12b..0f49324bc 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -12,14 +12,33 @@ import { } from '../../../src/core/public'; import { PLUGIN_NAME, ROUTES } from './utils/constants'; import { SecurityAnalyticsPluginSetup, SecurityAnalyticsPluginStart } from './index'; +import { Plugin as DataPublicPlugin } from '../../../src/plugins/data/public'; + +export interface SecurityAnalyticsPluginSetupDeps { + data: DataPublicPlugin; +} +export interface SecurityAnalyticsPluginStartDeps { + data: DataPublicPlugin; +} export class SecurityAnalyticsPlugin - implements Plugin { + implements + Plugin< + SecurityAnalyticsPluginSetup, + SecurityAnalyticsPluginStart, + SecurityAnalyticsPluginSetupDeps, + SecurityAnalyticsPluginStartDeps + > { constructor(private readonly initializerContext: PluginInitializerContext) { // can retrieve config from initializerContext } - public setup(core: CoreSetup): SecurityAnalyticsPluginSetup { + private plugins?: SecurityAnalyticsPluginStartDeps; + + public setup( + core: CoreSetup, + plugins: SecurityAnalyticsPluginSetupDeps + ): SecurityAnalyticsPluginSetup { core.application.register({ id: PLUGIN_NAME, title: 'Security Analytics', @@ -32,13 +51,17 @@ export class SecurityAnalyticsPlugin mount: async (params: AppMountParameters) => { const { renderApp } = await import('./security_analytics_app'); const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params, ROUTES.LANDING_PAGE); + return renderApp(coreStart, params, ROUTES.LANDING_PAGE, this.plugins); }, }); return {}; } - public start(core: CoreStart): SecurityAnalyticsPluginStart { + public start( + core: CoreStart, + plugins: SecurityAnalyticsPluginStartDeps + ): SecurityAnalyticsPluginStart { + this.plugins = plugins; return {}; } } diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index 81ed30e26..f2feba859 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -11,7 +11,7 @@ import { AlertsService, NotificationsService, ServicesContext, - SavedObjectsService, + IndexPatternsService, } from './services'; import { DarkModeContext } from './components/DarkMode'; import Main from './pages/Main'; @@ -24,8 +24,14 @@ import OpenSearchService from './services/OpenSearchService'; import { BrowserServices } from './models/interfaces'; import FieldMappingService from './services/FieldMappingService'; import RuleService from './services/RuleService'; +import { SecurityAnalyticsPluginStartDeps } from './plugin'; -export function renderApp(coreStart: CoreStart, params: AppMountParameters, landingPage: string) { +export function renderApp( + coreStart: CoreStart, + params: AppMountParameters, + landingPage: string, + plugins?: SecurityAnalyticsPluginStartDeps +) { const { http, savedObjects } = coreStart; const detectorsService = new DetectorsService(http); @@ -36,7 +42,9 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters, land const alertsService = new AlertsService(http); const ruleService = new RuleService(http); const notificationsService = new NotificationsService(http); - const savedObjectsService = new SavedObjectsService(savedObjects); + const indexPatternsService = plugins + ? new IndexPatternsService((plugins.data as any).indexPatterns) + : undefined; const services: BrowserServices = { detectorsService, @@ -47,7 +55,7 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters, land ruleService, alertService: alertsService, notificationsService, - savedObjectsService, + indexPatternsService, }; const isDarkMode = coreStart.uiSettings.get('theme:darkMode') || false; diff --git a/public/services/IndexPatternsService.ts b/public/services/IndexPatternsService.ts new file mode 100644 index 000000000..1c4a879fe --- /dev/null +++ b/public/services/IndexPatternsService.ts @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + GetFieldsOptions, + IndexPatternSpec, + IndexPatternsService as CoreIndexPatternsService, +} from '../../../../src/plugins/data/common/index_patterns'; + +export default class IndexPatternsService { + private coreIndexPatternsService: CoreIndexPatternsService; + + constructor(coreIndexPatternsService: CoreIndexPatternsService) { + this.coreIndexPatternsService = coreIndexPatternsService; + } + + async getFieldsForWildcard(options: GetFieldsOptions) { + return this.coreIndexPatternsService.getFieldsForWildcard(options); + } + + async createAndSave(spec: IndexPatternSpec, override = false, skipFetchFields = false) { + return this.coreIndexPatternsService.createAndSave(spec, override, skipFetchFields); + } +} diff --git a/public/services/OpenSearchService.ts b/public/services/OpenSearchService.ts index c88047b05..a9eeab185 100644 --- a/public/services/OpenSearchService.ts +++ b/public/services/OpenSearchService.ts @@ -57,28 +57,4 @@ export default class OpenSearchService { return Promise.resolve(indexPatterns); }; - - getFieldsForWildcard(options: GetFieldsOptions = {}) { - const { pattern, metaFields, type, params, dataSourceId } = options; - - let query; - - if (type) { - query = { - pattern, - meta_fields: metaFields, - params: JSON.stringify(params), - data_source: dataSourceId, - }; - } else { - query = { - pattern, - meta_fields: metaFields, - data_source: dataSourceId, - }; - } - - let url = `../api/index_patterns/_fields_for_wildcard`; - return this.httpClient.get(url, { query }); - } } diff --git a/public/services/SavedObjectsService.ts b/public/services/SavedObjectsService.ts deleted file mode 100644 index 4715dd8d8..000000000 --- a/public/services/SavedObjectsService.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { SavedObjectsStart } from 'opensearch-dashboards/public'; - -export default class SavedObjectsService { - private savedObjectsStart: SavedObjectsStart; - - constructor(savedObjectsStart: SavedObjectsStart) { - this.savedObjectsStart = savedObjectsStart; - } - - createIndexPattern = async (title: string, timeFieldName: string): Promise => { - const attributes = { - title, - timeFieldName, - fields: - '[{"count":0,"name":"AccountName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountName"}}},{"count":0,"name":"AccountType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"AccountType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"AccountType"}}},{"count":0,"name":"Category","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Category.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Category"}}},{"count":0,"name":"Channel","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Channel.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Channel"}}},{"count":0,"name":"CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Domain.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Domain"}}},{"count":0,"name":"EventID","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventReceivedTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventTime","type":"date","esTypes":["date"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"EventType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"EventType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"EventType"}}},{"count":0,"name":"ExecutionProcessID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ExecutionThreadID","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"HostName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Image.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Image"}}},{"count":0,"name":"Initiated","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Initiated.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Initiated"}}},{"count":0,"name":"Keywords","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Keywords.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Keywords"}}},{"count":0,"name":"Message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Opcode.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Opcode"}}},{"count":0,"name":"OpcodeValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ProcessGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessGuid"}}},{"count":0,"name":"ProcessId","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProcessId.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProcessId"}}},{"count":0,"name":"ProviderGuid","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"ProviderGuid.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"ProviderGuid"}}},{"count":0,"name":"Provider_Name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryName"}}},{"count":0,"name":"QueryResults","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryResults.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryResults"}}},{"count":0,"name":"QueryStatus","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"QueryStatus.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"QueryStatus"}}},{"count":0,"name":"RecordNumber","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"ServiceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"Severity.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"Severity"}}},{"count":0,"name":"SeverityValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"SourceModuleName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleName"}}},{"count":0,"name":"SourceModuleType","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceModuleType.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceModuleType"}}},{"count":0,"name":"SourceName","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"SourceName.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"SourceName"}}},{"count":0,"name":"TargetObject","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"TargetObject.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"TargetObject"}}},{"count":0,"name":"TaskValue","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"UserID","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UserID.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UserID"}}},{"count":0,"name":"UtcTime","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"UtcTime.keyword","type":"string","esTypes":["keyword"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent":"UtcTime"}}},{"count":0,"name":"Version","type":"number","esTypes":["long"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"_id","type":"string","esTypes":["_id"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_index","type":"string","esTypes":["_index"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"count":0,"name":"_score","type":"number","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_source","type":"_source","esTypes":["_source"],"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"_type","type":"string","scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"event_uid","type":"number","esTypes":["integer"],"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"count":0,"name":"windows-event_data-CommandLine","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-hostname","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-message","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-provider-name","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"count":0,"name":"windows-servicename","type":"string","esTypes":["text"],"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false}]', - }; - const options = { - id: '', - references: [], - }; - await this.savedObjectsStart.client.create('index-pattern', attributes, options); - }; -} diff --git a/public/services/index.ts b/public/services/index.ts index 600f06862..9964f84a5 100644 --- a/public/services/index.ts +++ b/public/services/index.ts @@ -12,7 +12,7 @@ import AlertsService from './AlertsService'; import RuleService from './RuleService'; import IndexService from './IndexService'; import NotificationsService from './NotificationsService'; -import SavedObjectsService from './SavedObjectsService'; +import IndexPatternsService from './IndexPatternsService'; export { ServicesConsumer, @@ -25,5 +25,5 @@ export { RuleService, IndexService, NotificationsService, - SavedObjectsService, + IndexPatternsService, }; From 760da4c5d6cc8c4f3f90529ee1a4c03468fe4f2c Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Thu, 19 Jan 2023 19:53:35 +0100 Subject: [PATCH 06/14] improve ux for create index pattern Signed-off-by: Aleksandar Djindjic --- public/models/interfaces.ts | 9 ---- .../components/CreateIndexPatternForm.tsx | 42 ++++++++++++++++--- .../components/FindingDetailsFlyout.tsx | 6 --- public/services/OpenSearchService.ts | 1 - 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index b61c5d11a..9ea5a423c 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -34,15 +34,6 @@ export interface RuleOptions { tags: string[]; } -export interface GetFieldsOptions { - pattern?: string; - type?: string; - params?: any; - lookBack?: boolean; - metaFields?: string[]; - dataSourceId?: string; -} - export interface RulesSharedState { page: RulesPage; rulesOptions: RuleOptions[]; diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 2c988e470..feadd4288 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -14,6 +14,7 @@ import { EuiSpacer, EuiComboBox, EuiText, + EuiCallOut, } from '@elastic/eui'; import { IndexPatternsService } from '../../../services'; @@ -44,6 +45,8 @@ export const CreateIndexPatternForm: React.FC = ({ indexPatternsService, }) => { const [timeFileds, setTimeFields] = useState([]); + const [creatingIndexInProgress, setCreatingIndexInProgress] = useState(false); + const [createdIndex, setCreatedIndex] = useState<{ id?: string; title: string }>(); const getTimeFields = async (name: string): Promise => { if (!indexPatternsService) { @@ -70,7 +73,24 @@ export const CreateIndexPatternForm: React.FC = ({ }); }, [initialValue.name]); - return ( + return createdIndex ? ( + <> + +

You may now view surrounding documents within the index

+
+ + + { + submit(createdIndex?.id || ''); + }} + > + View surrounding documents + + + + ) : ( { @@ -97,18 +117,26 @@ export const CreateIndexPatternForm: React.FC = ({ return; } try { + setCreatingIndexInProgress(true); const newIndex = await indexPatternsService.createAndSave({ title: values.name, timeFieldName: values.timeField, }); - if (newIndex.id) { - submit(newIndex.id); - } + setCreatingIndexInProgress(false); + setCreatedIndex({ id: newIndex.id, title: newIndex.title }); + // if (newIndex.id) { + // submit(newIndex.id); + // } } catch (e) {} }} > {(props) => (
+ + An index pattern is required to view all surrounding documents within the index. Create + an index pattern to continue. + + @@ -167,7 +195,11 @@ export const CreateIndexPatternForm: React.FC = ({ Cancel - props.handleSubmit()}> + props.handleSubmit()} + > Create index pattern diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index 39f48a00c..7977ddbc5 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -295,11 +295,6 @@ export default class FindingDetailsFlyout extends Component< - - An index pattern is required to view all surrounding documents within the index. - Create an index pattern to continue. - - { - console.log(indexPatternId); this.setState({ ...this.state, indexPatternId, diff --git a/public/services/OpenSearchService.ts b/public/services/OpenSearchService.ts index a9eeab185..f1b1c6632 100644 --- a/public/services/OpenSearchService.ts +++ b/public/services/OpenSearchService.ts @@ -11,7 +11,6 @@ import { import { ServerResponse } from '../../server/models/types'; import { SearchResponse, Plugin } from '../../server/models/interfaces'; import { API } from '../../server/utils/constants'; -import { GetFieldsOptions } from '../models/interfaces'; export default class OpenSearchService { constructor( From 938e8e9911841cf7d0412d0eb0992aea1dc9f799 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Thu, 19 Jan 2023 20:32:00 +0100 Subject: [PATCH 07/14] rename props Signed-off-by: Aleksandar Djindjic --- .../components/CreateIndexPatternForm.tsx | 21 +++++++++---------- .../components/FindingDetailsFlyout.tsx | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index feadd4288..7a51fb6ec 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -33,15 +33,15 @@ export interface CreateIndexPatternFormProps { initialValue: { name: string; }; - submit: (values: string) => void; - cancel: () => void; + created: (values: string) => void; + close: () => void; indexPatternsService?: IndexPatternsService; } export const CreateIndexPatternForm: React.FC = ({ initialValue, - submit, - cancel, + created, + close, indexPatternsService, }) => { const [timeFileds, setTimeFields] = useState([]); @@ -83,7 +83,7 @@ export const CreateIndexPatternForm: React.FC = ({ { - submit(createdIndex?.id || ''); + created(createdIndex?.id || ''); }} > View surrounding documents @@ -122,12 +122,11 @@ export const CreateIndexPatternForm: React.FC = ({ title: values.name, timeFieldName: values.timeField, }); - setCreatingIndexInProgress(false); setCreatedIndex({ id: newIndex.id, title: newIndex.title }); - // if (newIndex.id) { - // submit(newIndex.id); - // } - } catch (e) {} + } catch (e) { + console.warn(e); + } + setCreatingIndexInProgress(false); }} > {(props) => ( @@ -192,7 +191,7 @@ export const CreateIndexPatternForm: React.FC = ({ - Cancel + close()}>Cancel + close={() => this.setState({ ...this.state, isCreateIndexPatternModalVisible: false }) } - submit={(indexPatternId) => { + created={(indexPatternId) => { this.setState({ ...this.state, indexPatternId, From 566a22ae7f17453c23e16da07aa239e71fa0d6ae Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Sat, 21 Jan 2023 09:56:24 +0100 Subject: [PATCH 08/14] submit button loading state from formik submiting Signed-off-by: Aleksandar Djindjic --- .../components/CreateIndexPatternForm.tsx | 11 ++--------- public/plugin.ts | 17 ++++++++--------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 7a51fb6ec..d29f4bdca 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -45,7 +45,6 @@ export const CreateIndexPatternForm: React.FC = ({ indexPatternsService, }) => { const [timeFileds, setTimeFields] = useState([]); - const [creatingIndexInProgress, setCreatingIndexInProgress] = useState(false); const [createdIndex, setCreatedIndex] = useState<{ id?: string; title: string }>(); const getTimeFields = async (name: string): Promise => { @@ -112,12 +111,10 @@ export const CreateIndexPatternForm: React.FC = ({ return errors; }} onSubmit={async (values, { setSubmitting }) => { - setSubmitting(false); if (!indexPatternsService) { return; } try { - setCreatingIndexInProgress(true); const newIndex = await indexPatternsService.createAndSave({ title: values.name, timeFieldName: values.timeField, @@ -126,7 +123,7 @@ export const CreateIndexPatternForm: React.FC = ({ } catch (e) { console.warn(e); } - setCreatingIndexInProgress(false); + setSubmitting(false); }} > {(props) => ( @@ -194,11 +191,7 @@ export const CreateIndexPatternForm: React.FC = ({ close()}>Cancel - props.handleSubmit()} - > + props.handleSubmit()}> Create index pattern diff --git a/public/plugin.ts b/public/plugin.ts index 0f49324bc..06b0d2d0a 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -33,8 +33,6 @@ export class SecurityAnalyticsPlugin // can retrieve config from initializerContext } - private plugins?: SecurityAnalyticsPluginStartDeps; - public setup( core: CoreSetup, plugins: SecurityAnalyticsPluginSetupDeps @@ -50,18 +48,19 @@ export class SecurityAnalyticsPlugin }, mount: async (params: AppMountParameters) => { const { renderApp } = await import('./security_analytics_app'); - const [coreStart] = await core.getStartServices(); - return renderApp(coreStart, params, ROUTES.LANDING_PAGE, this.plugins); + const [coreStart, depsStart] = await core.getStartServices(); + return renderApp( + coreStart, + params, + ROUTES.LANDING_PAGE, + depsStart as SecurityAnalyticsPluginStartDeps + ); }, }); return {}; } - public start( - core: CoreStart, - plugins: SecurityAnalyticsPluginStartDeps - ): SecurityAnalyticsPluginStart { - this.plugins = plugins; + public start(core: CoreStart): SecurityAnalyticsPluginStart { return {}; } } From 1cd30fb7cca94ae7e9224566f77c9d171084ba76 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Sat, 21 Jan 2023 10:15:16 +0100 Subject: [PATCH 09/14] typos and minor changes Signed-off-by: Aleksandar Djindjic --- .../components/CreateIndexPatternForm.tsx | 26 ++++++++++--------- public/services/IndexPatternsService.ts | 6 +---- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index d29f4bdca..4003c5e50 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -44,7 +44,7 @@ export const CreateIndexPatternForm: React.FC = ({ close, indexPatternsService, }) => { - const [timeFileds, setTimeFields] = useState([]); + const [timeFields, setTimeFields] = useState([]); const [createdIndex, setCreatedIndex] = useState<{ id?: string; title: string }>(); const getTimeFields = async (name: string): Promise => { @@ -79,14 +79,16 @@ export const CreateIndexPatternForm: React.FC = ({ - { - created(createdIndex?.id || ''); - }} - > - View surrounding documents - + + { + created(createdIndex?.id || ''); + }} + > + View surrounding documents + + ) : ( @@ -148,8 +150,8 @@ export const CreateIndexPatternForm: React.FC = ({ data-test-subj={'index_pattern_name_field'} onChange={async (e) => { props.handleChange('name')(e); - const fileds = await getTimeFields(e.target.value); - setTimeFields(fileds); + const fields = await getTimeFields(e.target.value); + setTimeFields(fields); props.setFieldValue('timeField', ''); }} onBlur={props.handleBlur('name')} @@ -170,7 +172,7 @@ export const CreateIndexPatternForm: React.FC = ({ isInvalid={props.touched.timeField && !!props.errors.timeField} placeholder="Select a time field" data-test-subj={'index_pattern_time_field_dropdown'} - options={timeFileds.map((field: string) => ({ value: field, label: field }))} + options={timeFields.map((field: string) => ({ value: field, label: field }))} singleSelection={{ asPlainText: true }} onChange={(e) => { props.handleChange('timeField')(e[0]?.value ? e[0].value : ''); diff --git a/public/services/IndexPatternsService.ts b/public/services/IndexPatternsService.ts index 1c4a879fe..309f8fd16 100644 --- a/public/services/IndexPatternsService.ts +++ b/public/services/IndexPatternsService.ts @@ -10,11 +10,7 @@ import { } from '../../../../src/plugins/data/common/index_patterns'; export default class IndexPatternsService { - private coreIndexPatternsService: CoreIndexPatternsService; - - constructor(coreIndexPatternsService: CoreIndexPatternsService) { - this.coreIndexPatternsService = coreIndexPatternsService; - } + constructor(private coreIndexPatternsService: CoreIndexPatternsService) {} async getFieldsForWildcard(options: GetFieldsOptions) { return this.coreIndexPatternsService.getFieldsForWildcard(options); From ab586e65dea1a4931f9acd9447f6c158258ad96a Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Sun, 22 Jan 2023 13:38:15 +0100 Subject: [PATCH 10/14] indexPatternsService marked as mandatory dep Signed-off-by: Aleksandar Djindjic --- public/models/interfaces.ts | 2 +- public/pages/Findings/components/CreateIndexPatternForm.tsx | 5 +---- public/pages/Findings/components/FindingDetailsFlyout.tsx | 2 +- .../Findings/components/FindingsTable/FindingsTable.tsx | 2 +- public/pages/Findings/containers/Findings/Findings.tsx | 2 +- public/security_analytics_app.tsx | 6 ++---- 6 files changed, 7 insertions(+), 12 deletions(-) diff --git a/public/models/interfaces.ts b/public/models/interfaces.ts index 9ea5a423c..2cea676f1 100644 --- a/public/models/interfaces.ts +++ b/public/models/interfaces.ts @@ -24,7 +24,7 @@ export interface BrowserServices { alertService: AlertsService; ruleService: RuleService; notificationsService: NotificationsService; - indexPatternsService?: IndexPatternsService; + indexPatternsService: IndexPatternsService; } export interface RuleOptions { diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 4003c5e50..d73fc8895 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -35,7 +35,7 @@ export interface CreateIndexPatternFormProps { }; created: (values: string) => void; close: () => void; - indexPatternsService?: IndexPatternsService; + indexPatternsService: IndexPatternsService; } export const CreateIndexPatternForm: React.FC = ({ @@ -113,9 +113,6 @@ export const CreateIndexPatternForm: React.FC = ({ return errors; }} onSubmit={async (values, { setSubmitting }) => { - if (!indexPatternsService) { - return; - } try { const newIndex = await indexPatternsService.createAndSave({ title: values.name, diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index d394e7566..81a3b2d48 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -42,7 +42,7 @@ interface FindingDetailsFlyoutProps { backButton?: React.ReactNode; allRules: { [id: string]: RuleSource }; opensearchService: OpenSearchService; - indexPatternsService?: IndexPatternsService; + indexPatternsService: IndexPatternsService; closeFlyout: () => void; } diff --git a/public/pages/Findings/components/FindingsTable/FindingsTable.tsx b/public/pages/Findings/components/FindingsTable/FindingsTable.tsx index 475a34a25..3aa3ae241 100644 --- a/public/pages/Findings/components/FindingsTable/FindingsTable.tsx +++ b/public/pages/Findings/components/FindingsTable/FindingsTable.tsx @@ -39,7 +39,7 @@ interface FindingsTableProps extends RouteComponentProps { onRefresh: () => void; onFindingsFiltered: (findings: FindingItemType[]) => void; hasNotificationsPlugin: boolean; - indexPatternsService?: IndexPatternsService; + indexPatternsService: IndexPatternsService; } interface FindingsTableState { diff --git a/public/pages/Findings/containers/Findings/Findings.tsx b/public/pages/Findings/containers/Findings/Findings.tsx index 27afa1814..b3c357ea7 100644 --- a/public/pages/Findings/containers/Findings/Findings.tsx +++ b/public/pages/Findings/containers/Findings/Findings.tsx @@ -60,7 +60,7 @@ interface FindingsProps extends RouteComponentProps { detectorService: DetectorsService; findingsService: FindingsService; notificationsService: NotificationsService; - indexPatternsService?: IndexPatternsService; + indexPatternsService: IndexPatternsService; opensearchService: OpenSearchService; ruleService: RuleService; notifications: NotificationsStart; diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index f2feba859..9006b4b65 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -30,7 +30,7 @@ export function renderApp( coreStart: CoreStart, params: AppMountParameters, landingPage: string, - plugins?: SecurityAnalyticsPluginStartDeps + depsStart: SecurityAnalyticsPluginStartDeps ) { const { http, savedObjects } = coreStart; @@ -42,9 +42,7 @@ export function renderApp( const alertsService = new AlertsService(http); const ruleService = new RuleService(http); const notificationsService = new NotificationsService(http); - const indexPatternsService = plugins - ? new IndexPatternsService((plugins.data as any).indexPatterns) - : undefined; + const indexPatternsService = new IndexPatternsService((depsStart.data as any).indexPatterns); const services: BrowserServices = { detectorsService, From f88342614ee9ccf9d1a319ff78d922e4823d5736 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Sun, 22 Jan 2023 13:58:47 +0100 Subject: [PATCH 11/14] better typing on injected dependencies Signed-off-by: Aleksandar Djindjic --- public/plugin.ts | 15 +++++---------- public/security_analytics_app.tsx | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index 06b0d2d0a..89c02359c 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -12,13 +12,13 @@ import { } from '../../../src/core/public'; import { PLUGIN_NAME, ROUTES } from './utils/constants'; import { SecurityAnalyticsPluginSetup, SecurityAnalyticsPluginStart } from './index'; -import { Plugin as DataPublicPlugin } from '../../../src/plugins/data/public'; +import { DataPublicPluginStart, DataPublicPluginSetup } from '../../../src/plugins/data/public'; export interface SecurityAnalyticsPluginSetupDeps { - data: DataPublicPlugin; + data: DataPublicPluginSetup; } export interface SecurityAnalyticsPluginStartDeps { - data: DataPublicPlugin; + data: DataPublicPluginStart; } export class SecurityAnalyticsPlugin @@ -34,7 +34,7 @@ export class SecurityAnalyticsPlugin } public setup( - core: CoreSetup, + core: CoreSetup, plugins: SecurityAnalyticsPluginSetupDeps ): SecurityAnalyticsPluginSetup { core.application.register({ @@ -49,12 +49,7 @@ export class SecurityAnalyticsPlugin mount: async (params: AppMountParameters) => { const { renderApp } = await import('./security_analytics_app'); const [coreStart, depsStart] = await core.getStartServices(); - return renderApp( - coreStart, - params, - ROUTES.LANDING_PAGE, - depsStart as SecurityAnalyticsPluginStartDeps - ); + return renderApp(coreStart, params, ROUTES.LANDING_PAGE, depsStart); }, }); return {}; diff --git a/public/security_analytics_app.tsx b/public/security_analytics_app.tsx index 9006b4b65..bf8628e6c 100644 --- a/public/security_analytics_app.tsx +++ b/public/security_analytics_app.tsx @@ -42,7 +42,7 @@ export function renderApp( const alertsService = new AlertsService(http); const ruleService = new RuleService(http); const notificationsService = new NotificationsService(http); - const indexPatternsService = new IndexPatternsService((depsStart.data as any).indexPatterns); + const indexPatternsService = new IndexPatternsService(depsStart.data.indexPatterns); const services: BrowserServices = { detectorsService, From 78888f440da68ba791711d6829207e5cbf5afedb Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 24 Jan 2023 19:14:59 +0100 Subject: [PATCH 12/14] cypress test for create index pattern Signed-off-by: Aleksandar Djindjic --- cypress/integration/4_findings.spec.js | 29 +++++++++++++++++++ .../components/CreateIndexPatternForm.tsx | 7 ++++- .../components/FindingDetailsFlyout.tsx | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/cypress/integration/4_findings.spec.js b/cypress/integration/4_findings.spec.js index cd8025433..986db7afe 100644 --- a/cypress/integration/4_findings.spec.js +++ b/cypress/integration/4_findings.spec.js @@ -88,6 +88,35 @@ describe('Findings', () => { cy.get('.euiFlexItem--flexGrowZero > .euiButtonIcon').click({ force: true }); }); + it('displays finding details and create an index pattern from flyout', () => { + // filter table to show only sample_detector findings + cy.triggerSearchField('Search findings', 'sample_detector'); + + // Click findingId to trigger Finding details flyout + cy.getTableFirstRow('[data-test-subj="finding-details-flyout-button"]').then(($el) => { + cy.get($el).click({ force: true }); + }); + + cy.get('[data-test-subj="finding-details-flyout-view-surrounding-documents"]') + .contains('View surrounding documents') + .click({ force: true }); + + cy.contains('Create index pattern to view documents'); + + cy.get( + `[data-test-subj="index_pattern_time_field_dropdown"] [data-test-subj="comboBoxSearchInput"]` + ).type('EventTime'); + + cy.get('[data-test-subj="index_pattern_form_submit_button"]') + .contains('Create index pattern') + .click({ force: true }); + + cy.contains('cypress-test-windows* has been successfully created'); + + // Close Flyout + cy.get('.euiFlexItem--flexGrowZero > .euiButtonIcon').click({ force: true }); + }); + it('allows user to view details about rules that were triggered', () => { // filter table to show only sample_detector findings cy.triggerSearchField('Search findings', 'sample_detector'); diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index d73fc8895..651cd31c2 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -190,7 +190,12 @@ export const CreateIndexPatternForm: React.FC = ({ close()}>Cancel - props.handleSubmit()}> + props.handleSubmit()} + > Create index pattern diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index 81a3b2d48..68959bca3 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -225,6 +225,7 @@ export default class FindingDetailsFlyout extends Component< { if (indexPatternId) { window.open( From 6f90a74e309450120f462f28689364ba251cdd0d Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Tue, 24 Jan 2023 22:17:24 +0100 Subject: [PATCH 13/14] align cypress test with new convention Signed-off-by: Aleksandar Djindjic --- cypress/integration/4_findings.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/integration/4_findings.spec.js b/cypress/integration/4_findings.spec.js index 6cc801fd2..670423071 100644 --- a/cypress/integration/4_findings.spec.js +++ b/cypress/integration/4_findings.spec.js @@ -90,7 +90,7 @@ describe('Findings', () => { it('displays finding details and create an index pattern from flyout', () => { // filter table to show only sample_detector findings - cy.triggerSearchField('Search findings', 'sample_detector'); + cy.get(`input[placeholder="Search findings"]`).ospSearch('sample_detector'); // Click findingId to trigger Finding details flyout cy.getTableFirstRow('[data-test-subj="finding-details-flyout-button"]').then(($el) => { From 0bfba74b97f823934e811def932a697f2d14f0a7 Mon Sep 17 00:00:00 2001 From: Aleksandar Djindjic Date: Thu, 26 Jan 2023 00:11:48 +0100 Subject: [PATCH 14/14] useCallback event handler wrapper Signed-off-by: Aleksandar Djindjic --- .../components/CreateIndexPatternForm.tsx | 39 ++++++++++--------- .../components/FindingDetailsFlyout.tsx | 2 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/public/pages/Findings/components/CreateIndexPatternForm.tsx b/public/pages/Findings/components/CreateIndexPatternForm.tsx index 651cd31c2..e7bf2d1bf 100644 --- a/public/pages/Findings/components/CreateIndexPatternForm.tsx +++ b/public/pages/Findings/components/CreateIndexPatternForm.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { Formik, Form, FormikErrors } from 'formik'; import { EuiFlexGroup, @@ -47,24 +47,27 @@ export const CreateIndexPatternForm: React.FC = ({ const [timeFields, setTimeFields] = useState([]); const [createdIndex, setCreatedIndex] = useState<{ id?: string; title: string }>(); - const getTimeFields = async (name: string): Promise => { - if (!indexPatternsService) { - return []; - } - - return indexPatternsService - .getFieldsForWildcard({ - pattern: `${name}`, - metaFields: ['_source', '_id', '_type', '_index', '_score'], - params: {}, - }) - .then((res) => { - return res.filter((f) => f.type === 'date').map((f) => f.name); - }) - .catch(() => { + const getTimeFields = useCallback( + async (name: string): Promise => { + if (!indexPatternsService) { return []; - }); - }; + } + + return indexPatternsService + .getFieldsForWildcard({ + pattern: `${name}`, + metaFields: ['_source', '_id', '_type', '_index', '_score'], + params: {}, + }) + .then((res) => { + return res.filter((f) => f.type === 'date').map((f) => f.name); + }) + .catch(() => { + return []; + }); + }, + [initialValue] + ); useEffect(() => { getTimeFields(initialValue.name).then((fields) => { diff --git a/public/pages/Findings/components/FindingDetailsFlyout.tsx b/public/pages/Findings/components/FindingDetailsFlyout.tsx index f2e01659d..41b835154 100644 --- a/public/pages/Findings/components/FindingDetailsFlyout.tsx +++ b/public/pages/Findings/components/FindingDetailsFlyout.tsx @@ -223,7 +223,7 @@ export default class FindingDetailsFlyout extends Component<

Documents

- + {