Skip to content

Commit

Permalink
Feature] deleting detectors should delete all related dashboards (inc…
Browse files Browse the repository at this point in the history
…luding index patterns and visualisations) (#515)

* Update detector details component #504

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

* [FEATURE] Deleting detectors should delete all related dashboards (including index-patterns and visualisations) #509

Signed-off-by: Jovan Cvetkovic <[email protected]>

---------

Signed-off-by: Jovan Cvetkovic <[email protected]>
  • Loading branch information
jovancvetkovic3006 authored Apr 18, 2023
1 parent b7efb33 commit 88bed3e
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 34 deletions.
26 changes: 26 additions & 0 deletions cypress/fixtures/integration_tests/rule/create_network_rule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"id": "25b9c01c-350d-4b95-bed1-836d04a4f326",
"category": "network",
"title": "Cypress Network Rule",
"description": "Detects network changes",
"status": "experimental",
"author": "Cypress Tests",
"references": [
{
"value": ""
}
],
"tags": [
{
"value": "network.low"
}
],
"log_source": "",
"detection": "selection:\n keywords:\n - erase\n - delete\n - YXC\ncondition: selection",
"level": "low",
"false_positives": [
{
"value": ""
}
]
}
39 changes: 39 additions & 0 deletions cypress/fixtures/sample_network_index_settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"path": {
"type": "text"
},
"keywords": {
"type": "text"
},
"operation": {
"type": "text"
},
"action": {
"type": "text"
},
"dst_port": {
"type": "integer"
},
"timeframe": {
"type": "text"
},
"user_agent": {
"type": "text"
},
"method": {
"type": "text"
}
}
},
"settings": {
"index": {
"number_of_shards": "1",
"number_of_replicas": "1"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,14 @@ export const AlertTriggerView: React.FC<AlertTriggerViewProps> = ({
<h5>If any detection rule matches</h5>
</EuiTitle>
<EuiSpacer size={'m'} />
{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 },
])}
<EuiSpacer size="xl" />

<EuiTitle size="s">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
import { ContentPanel } from '../../../../components/ContentPanel';
import { createTextDetailsGroup, parseSchedule } from '../../../../utils/helpers';
import moment from 'moment';
import { DEFAULT_EMPTY_DATA } from '../../../../utils/constants';
import { DEFAULT_EMPTY_DATA, logTypesWithDashboards } from '../../../../utils/constants';
import { Detector } from '../../../../../types';

export interface DetectorBasicDetailsViewProps {
Expand Down Expand Up @@ -85,8 +85,10 @@ export const DetectorBasicDetailsView: React.FC<DetectorBasicDetailsViewProps> =
{`${name} summary`}
<EuiIcon type={'popout'} />
</EuiLink>
) : (
) : !logTypesWithDashboards.has(detector_type) ? (
'Not available for this log type'
) : (
'-'
)) as any,
},
])}
Expand Down
26 changes: 25 additions & 1 deletion public/pages/Detectors/containers/Detector/DetectorDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -47,6 +47,7 @@ export interface DetectorDetailsProps
detectorService: DetectorsService;
notifications: NotificationsStart;
savedObjectsService: ISavedObjectsService;
indexPatternsService: IndexPatternsService;
}

export interface DetectorDetailsState {
Expand Down Expand Up @@ -257,6 +258,11 @@ export class DetectorDetails extends React.Component<DetectorDetailsProps, Detec
BREADCRUMBS.DETECTORS,
BREADCRUMBS.DETECTORS_DETAILS(detector._source.name, detector._id),
]);

const dashboard = await this.props.savedObjectsService.getDashboard(detectorId);
if (dashboard?.id) {
this.setState({ dashboardId: dashboard.id });
}
} else {
errorNotificationToast(notifications, 'retrieve', 'detector', response.error);
}
Expand All @@ -282,10 +288,28 @@ export class DetectorDetails extends React.Component<DetectorDetailsProps, Detec
const { detectorService, notifications } = this.props;
const detectorId = this.detectorHit._id;
try {
const dashboard = await this.props.savedObjectsService.getDashboard(detectorId);
if (dashboard) {
// delete dashboard
dashboard.references?.map(async (ref) => {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ exports[`<DetectorDetails /> spec renders the component 1`] = `
}
savedObjectsService={
SavedObjectService {
"deleteDashboard": [Function],
"deleteVisualization": [Function],
"getDashboard": [Function],
"getDashboards": [Function],
"indexService": undefined,
"savedObjectsClient": Object {
Expand Down Expand Up @@ -914,6 +917,9 @@ exports[`<DetectorDetails /> spec renders the component 1`] = `
}
savedObjectsService={
SavedObjectService {
"deleteDashboard": [Function],
"deleteVisualization": [Function],
"getDashboard": [Function],
"getDashboards": [Function],
"indexService": undefined,
"savedObjectsClient": Object {
Expand Down Expand Up @@ -1325,6 +1331,9 @@ exports[`<DetectorDetails /> spec renders the component 1`] = `
onEditClicked={[Function]}
savedObjectsService={
SavedObjectService {
"deleteDashboard": [Function],
"deleteVisualization": [Function],
"getDashboard": [Function],
"getDashboards": [Function],
"indexService": undefined,
"savedObjectsClient": Object {
Expand Down Expand Up @@ -2308,6 +2317,13 @@ exports[`<DetectorDetails /> spec renders the component 1`] = `
</EuiPanel>
</ContentPanel>
</DetectorBasicDetailsView>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<DetectorRulesView
detector={
Object {
Expand Down Expand Up @@ -2707,6 +2723,9 @@ exports[`<DetectorDetails /> spec renders the component 1`] = `
onEditClicked={[Function]}
savedObjectsService={
SavedObjectService {
"deleteDashboard": [Function],
"deleteVisualization": [Function],
"getDashboard": [Function],
"getDashboards": [Function],
"indexService": undefined,
"savedObjectsClient": Object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DetectorRulesView } from '../../components/DetectorRulesView/DetectorRu
import { RuleItem } from '../../../CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { Detector } from '../../../../../types';
import { EuiSpacer } from '@elastic/eui';

export interface DetectorDetailsViewProps {
detector: Detector;
Expand Down Expand Up @@ -40,13 +41,16 @@ export class DetectorDetailsView extends React.Component<
isEditable = true,
} = this.props;
const detectorRules = (
<DetectorRulesView
{...this.props}
detector={detector}
rulesCanFold={rulesCanFold}
onEditClicked={editDetectorRules}
isEditable={isEditable}
/>
<>
<EuiSpacer size="m" />
<DetectorRulesView
{...this.props}
detector={detector}
rulesCanFold={rulesCanFold}
onEditClicked={editDetectorRules}
isEditable={isEditable}
/>
</>
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,13 @@ exports[`<DetectorDetailsView /> spec renders the component 1`] = `
</EuiPanel>
</ContentPanel>
</DetectorBasicDetailsView>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<DetectorRulesView
detector={
Object {
Expand Down
1 change: 1 addition & 0 deletions public/pages/Main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ export default class Main extends Component<MainProps, MainState> {
<DetectorDetails
detectorService={services.detectorsService}
savedObjectsService={services.savedObjectsService}
indexPatternsService={services.indexPatternsService}
{...props}
notifications={core?.notifications}
/>
Expand Down
31 changes: 31 additions & 0 deletions public/services/IndexPatternsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand All @@ -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<IndexPatternSavedObjectAttrs>[] | null | undefined
> => {
const indexPatterns = await this.coreIndexPatternsService.getCache();

return Promise.resolve(indexPatterns);
};

public getIndexPattern = async (
detectorId: string
): Promise<Promise<SavedObject<IndexPatternSavedObjectAttrs>> | Promise<undefined>> => {
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);
}
33 changes: 32 additions & 1 deletion public/services/SavedObjectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
{
Expand All @@ -51,6 +58,7 @@ export default class SavedObjectService implements ISavedObjectsService {
},
{
...savedObjectConfig['index-pattern'],
references: detectorReferences,
}
);

Expand Down Expand Up @@ -130,6 +138,23 @@ export default class SavedObjectService implements ISavedObjectsService {
return Promise.resolve(dashboards);
};

public getDashboard = async (
detectorId: string
): Promise<Promise<SimpleSavedObject<SavedObjectReference>> | Promise<undefined>> => {
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<SimpleSavedObject<{ title: string }>[]> {
const indexPatterns = await this.savedObjectsClient
.find<{ title: string }>({
Expand All @@ -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);
}
Loading

0 comments on commit 88bed3e

Please sign in to comment.