Skip to content

Commit

Permalink
genericizes duplicate discovery functions, allows creation of non-dup…
Browse files Browse the repository at this point in the history
…licated rules even when duplicates are discovered, keeps same return type signature, updates relevant test for duplicates in request payload
  • Loading branch information
dhurley14 committed Feb 7, 2020
1 parent 7000f8b commit 59df66d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
} from '../__mocks__/request_responses';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
import { createRulesBulkRoute } from './create_rules_bulk_route';
import { BulkError } from '../utils';
import { OutputRuleAlertRest } from '../../types';

describe('create_rules_bulk', () => {
let { server, alertsClient, actionsClient, elasticsearch } = createMockServer();
Expand Down Expand Up @@ -136,6 +138,7 @@ describe('create_rules_bulk', () => {
payload: [typicalPayload(), typicalPayload()],
};
const { payload } = await server.inject(request);
expect(JSON.parse(payload).error.status_code).toBe(409);
const output: Array<BulkError | Partial<OutputRuleAlertRest>> = JSON.parse(payload);
expect(output.some(item => item.error?.status_code === 409)).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { createRules } from '../../rules/create_rules';
import { BulkRulesRequest } from '../../rules/types';
import { ServerFacade } from '../../../../types';
import { readRules } from '../../rules/read_rules';
import { transformOrBulkError } from './utils';
import { transformOrBulkError, getMapDuplicates, getDuplicates } from './utils';
import { getIndexExists } from '../../index/get_index_exists';
import {
callWithRequestFactory,
Expand Down Expand Up @@ -48,34 +48,8 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
return headers.response().code(404);
}

// Determine if duplicate rule_ids exist in the request
// before attempting to create them.
const mapDuplicates = request.payload.reduce<Map<string, number>>((acc, rule) => {
if (rule.rule_id != null && acc.has(rule.rule_id)) {
const totalView = acc.get(rule.rule_id) ?? 1;
acc.set(rule.rule_id, totalView + 1);
} else if (rule.rule_id != null) {
acc.set(rule.rule_id, 1);
}
return acc;
}, new Map());

const hasDuplicates = Array.from(mapDuplicates.values()).some(i => i > 1);
if (hasDuplicates) {
const duplicates = Array.from(mapDuplicates.entries())
.reduce<string[]>((acc, [key, val]) => {
if (val > 1) {
return [...acc, key];
}
return acc;
}, [])
.join(', ');
return createBulkErrorObject({
ruleId: duplicates,
statusCode: 409,
message: `rule_id: "${duplicates}" already exists`,
});
}
const mappedDuplicates = getMapDuplicates(request.payload, 'rule_id');
const dupes = getDuplicates(mappedDuplicates);

const rules = await Promise.all(
request.payload.map(async payloadRule => {
Expand Down Expand Up @@ -106,6 +80,13 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou
timeline_title: timelineTitle,
version,
} = payloadRule;
if (ruleId != null && dupes?.includes(ruleId)) {
return createBulkErrorObject({
ruleId,
statusCode: 409,
message: `rule_id: "${ruleId}" already exists`,
});
}
const ruleIdOrUuid = ruleId ?? uuid.v4();
try {
const finalIndex = outputIndex != null ? outputIndex : getIndex(request, server);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,32 @@ export const transformOrImportError = (
});
}
};

export const getMapDuplicates = (
arr: Array<{ [key: string]: string }>,
prop: string
): Map<string, number> =>
arr.reduce<Map<string, number>>((acc, item) => {
if (item[prop] != null && acc.has(item[prop])) {
const totalView = acc.get(item[prop]) ?? 1;
acc.set(item[prop], totalView + 1);
} else if (item[prop] != null) {
acc.set(item[prop], 1);
}
return acc;
}, new Map());

export const getDuplicates = (someMap: Map<string, number>): string | null => {
const hasDuplicates = Array.from(someMap.values()).some(i => i > 1);
if (hasDuplicates) {
return Array.from(someMap.entries())
.reduce<string[]>((acc, [key, val]) => {
if (val > 1) {
return [...acc, key];
}
return acc;
}, [])
.join(', ');
}
return null;
};

0 comments on commit 59df66d

Please sign in to comment.