From 3ecdd08fad9bdd4e56941b74d8999e4b63e7b5da Mon Sep 17 00:00:00 2001 From: Jovan Cvetkovic Date: Wed, 12 Apr 2023 16:23:10 +0200 Subject: [PATCH] [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509 Signed-off-by: Jovan Cvetkovic --- .../AlertTriggerView/AlertTriggerView.tsx | 22 +++++-------- .../DetectorBasicDetailsView.tsx | 19 +++++------ .../containers/Detector/DetectorDetails.tsx | 26 ++++++++++++++- public/pages/Main/Main.tsx | 1 + public/services/IndexPatternsService.ts | 31 +++++++++++++++++ public/services/SavedObjectService.ts | 33 ++++++++++++++++++- public/store/DetectorsStore.tsx | 19 ++++++----- types/services/ISavedObjectsService.ts | 5 +++ 8 files changed, 120 insertions(+), 36 deletions(-) diff --git a/public/pages/Detectors/components/AlertTriggerView/AlertTriggerView.tsx b/public/pages/Detectors/components/AlertTriggerView/AlertTriggerView.tsx index 97d4ace6c..e0de40736 100644 --- a/public/pages/Detectors/components/AlertTriggerView/AlertTriggerView.tsx +++ b/public/pages/Detectors/components/AlertTriggerView/AlertTriggerView.tsx @@ -64,20 +64,14 @@ export const AlertTriggerView: React.FC = ({
If any detection rule matches
- {createTextDetailsGroup( - [ - { label: 'Log type', content: `${types[0]}` || DEFAULT_EMPTY_DATA }, - { label: 'Rule names', content: conditionRuleNames.join('\n') || DEFAULT_EMPTY_DATA }, - ], - 3 - )} - {createTextDetailsGroup( - [ - { label: 'Rule severities', content: sev_levels.join('\n') || DEFAULT_EMPTY_DATA }, - { label: 'Tags', content: tags.join('\n') || DEFAULT_EMPTY_DATA }, - ], - 3 - )} + {createTextDetailsGroup([ + { label: 'Log type', content: `${types[0]}` || DEFAULT_EMPTY_DATA }, + { label: 'Rule names', content: conditionRuleNames.join('\n') || DEFAULT_EMPTY_DATA }, + ])} + {createTextDetailsGroup([ + { label: 'Rule severities', content: sev_levels.join('\n') || DEFAULT_EMPTY_DATA }, + { label: 'Tags', content: tags.join('\n') || DEFAULT_EMPTY_DATA }, + ])} diff --git a/public/pages/Detectors/components/DetectorBasicDetailsView/DetectorBasicDetailsView.tsx b/public/pages/Detectors/components/DetectorBasicDetailsView/DetectorBasicDetailsView.tsx index a8eb24151..caa456869 100644 --- a/public/pages/Detectors/components/DetectorBasicDetailsView/DetectorBasicDetailsView.tsx +++ b/public/pages/Detectors/components/DetectorBasicDetailsView/DetectorBasicDetailsView.tsx @@ -49,7 +49,7 @@ export const DetectorBasicDetailsView: React.FC = ) : ( - 'Not available for this log type' + '-' )) as any, }, ]; @@ -68,16 +68,13 @@ export const DetectorBasicDetailsView: React.FC = } > - {createTextDetailsGroup(firstTextDetailsGroupEntries, 4)} - {createTextDetailsGroup( - [ - { label: 'Description', content: inputs[0].detector_input.description }, - { label: 'Detector schedule', content: detectorSchedule }, - { label: 'Created at', content: createdAt || DEFAULT_EMPTY_DATA }, - { label: 'Last updated time', content: lastUpdated || DEFAULT_EMPTY_DATA }, - ], - 4 - )} + {createTextDetailsGroup(firstTextDetailsGroupEntries)} + {createTextDetailsGroup([ + { label: 'Description', content: inputs[0].detector_input.description }, + { label: 'Detector schedule', content: detectorSchedule }, + { label: 'Created at', content: createdAt || DEFAULT_EMPTY_DATA }, + { label: 'Last updated time', content: lastUpdated || DEFAULT_EMPTY_DATA }, + ])} {rulesCanFold ? children : null} ); diff --git a/public/pages/Detectors/containers/Detector/DetectorDetails.tsx b/public/pages/Detectors/containers/Detector/DetectorDetails.tsx index c423817b6..e8f908645 100644 --- a/public/pages/Detectors/containers/Detector/DetectorDetails.tsx +++ b/public/pages/Detectors/containers/Detector/DetectorDetails.tsx @@ -26,7 +26,7 @@ import { DetectorDetailsView } from '../DetectorDetailsView/DetectorDetailsView' import { FieldMappingsView } from '../../components/FieldMappingsView/FieldMappingsView'; import { AlertTriggersView } from '../AlertTriggersView/AlertTriggersView'; import { RuleItem } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces'; -import { DetectorsService } from '../../../../services'; +import { DetectorsService, IndexPatternsService } from '../../../../services'; import { errorNotificationToast } from '../../../../utils/helpers'; import { NotificationsStart, SimpleSavedObject } from 'opensearch-dashboards/public'; import { ISavedObjectsService, ServerResponse } from '../../../../../types'; @@ -47,6 +47,7 @@ export interface DetectorDetailsProps detectorService: DetectorsService; notifications: NotificationsStart; savedObjectsService: ISavedObjectsService; + indexPatternsService: IndexPatternsService; } export interface DetectorDetailsState { @@ -257,6 +258,11 @@ export class DetectorDetails extends React.Component { + if (ref.type === 'visualization') { + await this.props.savedObjectsService.deleteVisualization(ref.id); + } + }); + await this.props.savedObjectsService.deleteDashboard(dashboard.id); + } + + const index = await this.props.indexPatternsService.getIndexPattern(detectorId); + if (index) { + await this.props.indexPatternsService.deleteIndexPattern(index.id); + } + const deleteRes = await detectorService.deleteDetector(detectorId); if (!deleteRes.ok) { errorNotificationToast(notifications, 'delete', 'detector', deleteRes.error); } else { + DataStore.detectors.deleteState(); + DataStore.detectors.clearNotifications(); this.props.history.push(ROUTES.DETECTORS); } } catch (e: any) { diff --git a/public/pages/Main/Main.tsx b/public/pages/Main/Main.tsx index 3590d214f..02a627e28 100644 --- a/public/pages/Main/Main.tsx +++ b/public/pages/Main/Main.tsx @@ -386,6 +386,7 @@ export default class Main extends Component { diff --git a/public/services/IndexPatternsService.ts b/public/services/IndexPatternsService.ts index 309f8fd16..69796a37e 100644 --- a/public/services/IndexPatternsService.ts +++ b/public/services/IndexPatternsService.ts @@ -8,6 +8,8 @@ import { IndexPatternSpec, IndexPatternsService as CoreIndexPatternsService, } from '../../../../src/plugins/data/common/index_patterns'; +import { IndexPatternSavedObjectAttrs } from '../../../../src/plugins/data/common/index_patterns/index_patterns'; +import { SavedObject } from '../../../../src/plugins/data/common'; export default class IndexPatternsService { constructor(private coreIndexPatternsService: CoreIndexPatternsService) {} @@ -19,4 +21,33 @@ export default class IndexPatternsService { async createAndSave(spec: IndexPatternSpec, override = false, skipFetchFields = false) { return this.coreIndexPatternsService.createAndSave(spec, override, skipFetchFields); } + + public getIndexPatterns = async (): Promise< + SavedObject[] | null | undefined + > => { + const indexPatterns = await this.coreIndexPatternsService.getCache(); + + return Promise.resolve(indexPatterns); + }; + + public getIndexPattern = async ( + detectorId: string + ): Promise> | Promise> => { + let indexPattern; + const indexPatterns = await this.getIndexPatterns(); + console.log('indexPatterns', indexPatterns); + indexPatterns?.some((indexRef) => { + if (indexRef.references.findIndex((reference) => reference.id === detectorId) > -1) { + indexPattern = indexRef; + return true; + } + + return false; + }); + + return indexPattern; + }; + + public deleteIndexPattern = async (indexPatternId: string) => + await this.coreIndexPatternsService.delete(indexPatternId); } diff --git a/public/services/SavedObjectService.ts b/public/services/SavedObjectService.ts index 6dd6d91e9..040a823cd 100644 --- a/public/services/SavedObjectService.ts +++ b/public/services/SavedObjectService.ts @@ -39,10 +39,17 @@ export default class SavedObjectService implements ISavedObjectsService { }, }; }); - const createAliasRes = await this.indexService.updateAliases({ + await this.indexService.updateAliases({ actions: indexActions, }); + const detectorReferences = [ + { + id: detectorId, + name: name, + type: 'detector-SA', + }, + ]; const indexPattern = await this.savedObjectsClient.create( savedObjectConfig['index-pattern'].type, { @@ -51,6 +58,7 @@ export default class SavedObjectService implements ISavedObjectsService { }, { ...savedObjectConfig['index-pattern'], + references: detectorReferences, } ); @@ -130,6 +138,23 @@ export default class SavedObjectService implements ISavedObjectsService { return Promise.resolve(dashboards); }; + public getDashboard = async ( + detectorId: string + ): Promise> | Promise> => { + let dashboard; + const dashboards = await this.getDashboards(); + dashboards?.some((dashRef) => { + if (dashRef.references.findIndex((reference) => reference.id === detectorId) > -1) { + dashboard = dashRef; + return true; + } + + return false; + }); + + return dashboard; + }; + public async getIndexPatterns(): Promise[]> { const indexPatterns = await this.savedObjectsClient .find<{ title: string }>({ @@ -141,4 +166,10 @@ export default class SavedObjectService implements ISavedObjectsService { return Promise.resolve(indexPatterns); } + + public deleteDashboard = async (dashboardId: string) => + await this.savedObjectsClient.delete('dashboard', dashboardId); + + public deleteVisualization = async (visualizationId: string) => + await this.savedObjectsClient.delete('visualization', visualizationId); } diff --git a/public/store/DetectorsStore.tsx b/public/store/DetectorsStore.tsx index 9377d06d7..666e7c589 100644 --- a/public/store/DetectorsStore.tsx +++ b/public/store/DetectorsStore.tsx @@ -132,6 +132,12 @@ export class DetectorsStore implements IDetectorsStore { delete this.state; }; + public clearNotifications = (): void => { + this.hideCallout(); + this.toasts = []; + this.showToastCallback([]); + }; + private showNotification = ( title: string, message?: string, @@ -258,15 +264,10 @@ export class DetectorsStore implements IDetectorsStore { if (dashboardResponse && dashboardResponse.ok) { dashboardId = dashboardResponse.response.id; } else { - const dashboards = await this.savedObjectsService.getDashboards(); - dashboards.some((dashboard) => { - if (dashboard.references.findIndex((reference) => reference.id === detectorId) > -1) { - dashboardId = dashboard.id; - return true; - } - - return false; - }); + const dashboard = await this.savedObjectsService.getDashboard(detectorId); + if (dashboard) { + dashboardId = dashboard.id; + } } } diff --git a/types/services/ISavedObjectsService.ts b/types/services/ISavedObjectsService.ts index 92902be38..f42c713b4 100644 --- a/types/services/ISavedObjectsService.ts +++ b/types/services/ISavedObjectsService.ts @@ -16,4 +16,9 @@ export interface ISavedObjectsService { getDashboards(): Promise< SimpleSavedObject<{ references: SavedObjectReference[]; id?: string }>[] >; + getDashboard( + detectorId: string + ): Promise> | Promise>; + deleteDashboard(detectorId: string): Promise; + deleteVisualization(detectorId: string): Promise; }