Skip to content

Commit

Permalink
update rule status saved object interfaces to play nicely with interf…
Browse files Browse the repository at this point in the history
…aces provided by saved objects module. Update tests to pass - Need to write new unit tests in an upcoming commit. Next commit will be cleanup from comments then new unit tests.
  • Loading branch information
dhurley14 committed Jan 13, 2020
1 parent c432f41 commit 74a6fba
Show file tree
Hide file tree
Showing 15 changed files with 177 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { ServerInjectOptions } from 'hapi';
import { SavedObjectsFindResponse } from 'kibana/server';
import { ActionResult } from '../../../../../../actions/server/types';
import { SignalsStatusRestParams, SignalsQueryRestParams } from '../../signals/types';
import {
Expand All @@ -15,7 +16,7 @@ import {
INTERNAL_RULE_ID_KEY,
INTERNAL_IMMUTABLE_KEY,
} from '../../../../../common/constants';
import { RuleAlertType } from '../../rules/types';
import { RuleAlertType, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { RuleAlertParamsRest } from '../../types';

export const fullRuleAlertParamsRest = (): RuleAlertParamsRest => ({
Expand Down Expand Up @@ -381,3 +382,10 @@ export const getMockPrivileges = () => ({
application: {},
isAuthenticated: false,
});

export const getFindResultStatus = (): SavedObjectsFindResponse<IRuleSavedAttributesSavedObjectAttributes> => ({
page: 1,
per_page: 1,
total: 0,
saved_objects: [],
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,28 @@ import {
createActionResult,
getCreateRequest,
typicalPayload,
getFindResultStatus,
} from '../__mocks__/request_responses';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';

describe('create_rules', () => {
let { server, alertsClient, actionsClient, elasticsearch } = createMockServer();
let {
server,
alertsClient,
actionsClient,
elasticsearch,
savedObjectsClient,
} = createMockServer();

beforeEach(() => {
jest.resetAllMocks();
({ server, alertsClient, actionsClient, elasticsearch } = createMockServer());
({
server,
alertsClient,
actionsClient,
elasticsearch,
savedObjectsClient,
} = createMockServer());
elasticsearch.getCluster = jest.fn().mockImplementation(() => ({
callWithRequest: jest.fn().mockImplementation(() => true),
}));
Expand All @@ -42,6 +55,7 @@ describe('create_rules', () => {
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
const { statusCode } = await server.inject(getCreateRequest());
expect(statusCode).toBe(200);
});
Expand Down Expand Up @@ -76,6 +90,7 @@ describe('create_rules', () => {
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
// missing rule_id should return 200 as it will be auto generated if not given
const { rule_id, ...noRuleId } = typicalPayload();
const request: ServerInjectOptions = {
Expand All @@ -92,6 +107,7 @@ describe('create_rules', () => {
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
const { type, ...noType } = typicalPayload();
const request: ServerInjectOptions = {
method: 'POST',
Expand All @@ -110,6 +126,7 @@ describe('create_rules', () => {
alertsClient.get.mockResolvedValue(getResult());
actionsClient.create.mockResolvedValue(createActionResult());
alertsClient.create.mockResolvedValue(getResult());
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
const { type, ...noType } = typicalPayload();
const request: ServerInjectOptions = {
method: 'POST',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Boom from 'boom';
import uuid from 'uuid';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
import { createRules } from '../../rules/create_rules';
import { RulesRequest } from '../../rules/types';
import { RulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { createRulesSchema } from '../schemas/create_rules_schema';
import { ServerFacade } from '../../../../types';
import { readRules } from '../../rules/read_rules';
Expand Down Expand Up @@ -121,17 +121,17 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute =
references,
version: 1,
});
const date = new Date().toISOString();
const ruleStatus = await savedObjectsClient.create(ruleStatusSavedObjectType, {
alertId: createdRule.id, // do a search for this id.
statusDate: date,
status: 'executing',
lastFailureAt: 'test-failure',
lastSuccessAt: 'test-success',
lastFailureMessage: 'test-failure',
lastSuccessMessage: 'test-failure',
const ruleStatuses = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 10,
sortField: 'statusDate',
sortOrder: 'desc',
search: `"${createdRule.id}"`,
searchFields: ['alertId'],
});
return transformOrError(createdRule, ruleStatus);
return transformOrError(createdRule, ruleStatuses.saved_objects[0]);
} catch (err) {
return transformError(err);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import {
getDeleteRequest,
getFindResultWithSingleHit,
getDeleteRequestById,
getFindResultStatus,
} from '../__mocks__/request_responses';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';

describe('delete_rules', () => {
let { server, alertsClient } = createMockServer();
let { server, alertsClient, savedObjectsClient } = createMockServer();

beforeEach(() => {
({ server, alertsClient } = createMockServer());
({ server, alertsClient, savedObjectsClient } = createMockServer());
deleteRulesRoute((server as unknown) as ServerFacade);
});

Expand All @@ -41,6 +42,8 @@ describe('delete_rules', () => {
alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
alertsClient.get.mockResolvedValue(getResult());
alertsClient.delete.mockResolvedValue({});
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
savedObjectsClient.delete.mockResolvedValue({});
const { statusCode } = await server.inject(getDeleteRequest());
expect(statusCode).toBe(200);
});
Expand All @@ -49,6 +52,8 @@ describe('delete_rules', () => {
alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
alertsClient.get.mockResolvedValue(getResult());
alertsClient.delete.mockResolvedValue({});
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
savedObjectsClient.delete.mockResolvedValue({});
const { statusCode } = await server.inject(getDeleteRequestById());
expect(statusCode).toBe(200);
});
Expand All @@ -57,6 +62,8 @@ describe('delete_rules', () => {
alertsClient.find.mockResolvedValue(getFindResult());
alertsClient.get.mockResolvedValue(getResult());
alertsClient.delete.mockResolvedValue({});
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
savedObjectsClient.delete.mockResolvedValue({});
const { statusCode } = await server.inject(getDeleteRequest());
expect(statusCode).toBe(404);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ServerFacade } from '../../../../types';
import { queryRulesSchema } from '../schemas/query_rules_schema';
import { getIdError, transformOrError } from './utils';
import { transformError } from '../utils';
import { QueryRequest } from '../../rules/types';
import { QueryRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';

export const createDeleteRulesRoute: Hapi.ServerRoute = {
Expand Down Expand Up @@ -46,12 +46,14 @@ export const createDeleteRulesRoute: Hapi.ServerRoute = {
id,
ruleId,
});
const ruleStatuses = await savedObjectsClient.find({
type: ruleStatusSavedObjectType,
perPage: 10,
search: `"${id}"`,
searchFields: ['alertId'],
});
const ruleStatuses = await savedObjectsClient.find<IRuleSavedAttributesSavedObjectAttributes>(
{
type: ruleStatusSavedObjectType,
perPage: 10,
search: `"${id}"`,
searchFields: ['alertId'],
}
);
ruleStatuses.saved_objects.forEach(async obj =>
savedObjectsClient.delete(ruleStatusSavedObjectType, obj.id)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Hapi from 'hapi';
import { isFunction } from 'lodash/fp';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
import { findRules } from '../../rules/find_rules';
import { FindRulesRequest } from '../../rules/types';
import { FindRulesRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { findRulesSchema } from '../schemas/find_rules_schema';
import { ServerFacade } from '../../../../types';
import { transformFindAlertsOrError } from './utils';
Expand Down Expand Up @@ -49,7 +49,7 @@ export const createFindRulesRoute: Hapi.ServerRoute = {
});
const ruleStatuses = await Promise.all(
rules.data.map(async rule => {
const results = await savedObjectsClient.find({
const results = await savedObjectsClient.find<IRuleSavedAttributesSavedObjectAttributes>({
type: ruleStatusSavedObjectType,
perPage: 10,
search: `"${rule.id}"`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { isFunction } from 'lodash/fp';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';
import { ServerFacade } from '../../../../types';
import { findRulesStatusesSchema } from '../schemas/find_rules_statuses_schema';
import { FindRulesStatusesRequest } from '../../rules/types';
import {
FindRulesStatusesRequest,
IRuleSavedAttributesSavedObjectAttributes,
} from '../../rules/types';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';

export const createFindRulesStatusRoute: Hapi.ServerRoute = {
Expand Down Expand Up @@ -44,7 +47,9 @@ export const createFindRulesStatusRoute: Hapi.ServerRoute = {
}
*/
const statuses = await query.ids.reduce(async (acc, id) => {
const lastFiveErrorsForId = await savedObjectsClient.find({
const lastFiveErrorsForId = await savedObjectsClient.find<
IRuleSavedAttributesSavedObjectAttributes
>({
type: ruleStatusSavedObjectType,
perPage: 10,
search: `"${id}"`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import {
getResult,
getReadRequest,
getFindResultWithSingleHit,
getFindResultStatus,
} from '../__mocks__/request_responses';
import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants';

describe('read_signals', () => {
let { server, alertsClient } = createMockServer();
let { server, alertsClient, savedObjectsClient } = createMockServer();

beforeEach(() => {
({ server, alertsClient } = createMockServer());
({ server, alertsClient, savedObjectsClient } = createMockServer());
readRulesRoute((server as unknown) as ServerFacade);
});

Expand All @@ -39,6 +40,7 @@ describe('read_signals', () => {
test('returns 200 when reading a single rule with a valid actionClient and alertClient', async () => {
alertsClient.find.mockResolvedValue(getFindResultWithSingleHit());
alertsClient.get.mockResolvedValue(getResult());
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
const { statusCode } = await server.inject(getReadRequest());
expect(statusCode).toBe(200);
});
Expand Down Expand Up @@ -72,6 +74,7 @@ describe('read_signals', () => {
alertsClient.find.mockResolvedValue(getFindResult());
alertsClient.get.mockResolvedValue(getResult());
alertsClient.delete.mockResolvedValue({});
savedObjectsClient.find.mockResolvedValue(getFindResultStatus());
const request: ServerInjectOptions = {
method: 'GET',
url: DETECTION_ENGINE_RULES_URL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { transformError } from '../utils';
import { readRules } from '../../rules/read_rules';
import { ServerFacade } from '../../../../types';
import { queryRulesSchema } from '../schemas/query_rules_schema';
import { QueryRequest } from '../../rules/types';
import { QueryRequest, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types';
import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings';

export const createReadRulesRoute: Hapi.ServerRoute = {
Expand Down Expand Up @@ -44,12 +44,16 @@ export const createReadRulesRoute: Hapi.ServerRoute = {
id,
ruleId,
});
const ruleStatuses = await savedObjectsClient.find({
type: ruleStatusSavedObjectType,
perPage: 10,
search: `"${id}"`,
searchFields: ['alertId'],
});
const ruleStatuses = await savedObjectsClient.find<IRuleSavedAttributesSavedObjectAttributes>(
{
type: ruleStatusSavedObjectType,
perPage: 10,
sortField: 'statusDate',
sortOrder: 'desc',
search: `"${id}"`,
searchFields: ['alertId'],
}
);
ruleStatuses.saved_objects.sort((a, b) => {
const dateA = new Date(a.attributes.statusDate);
const dateB = new Date(b.attributes.statusDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@

import Boom from 'boom';
import { pickBy } from 'lodash/fp';
import { SavedObject } from 'kibana/server';
import { INTERNAL_IDENTIFIER } from '../../../../../common/constants';
import {
RuleAlertType,
isAlertType,
isAlertTypes,
IRuleStatusSavedObject,
IRuleSavedAttributesSavedObjectAttributes,
isRuleStatusFindType,
isRuleStatusFindTypes,
isRuleStatusSavedObjectType,
Expand Down Expand Up @@ -71,7 +72,7 @@ export const transformTags = (tags: string[]): string[] => {
// those on the export
export const transformAlertToRule = (
alert: RuleAlertType,
ruleStatus?: IRuleStatusSavedObject
ruleStatus?: SavedObject<IRuleSavedAttributesSavedObjectAttributes>
): Partial<OutputRuleAlertRest> => {
return pickBy<OutputRuleAlertRest>((value: unknown) => value != null, {
created_at: alert.params.createdAt,
Expand Down Expand Up @@ -154,8 +155,12 @@ export const transformOrBulkError = (
alert: unknown,
ruleStatus?: unknown
): Partial<OutputRuleAlertRest> | BulkError => {
if (isAlertType(alert) && isRuleStatusFindType(ruleStatus)) {
return transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus);
if (isAlertType(alert)) {
if (isRuleStatusFindType(ruleStatus)) {
return transformAlertToRule(alert, ruleStatus?.saved_objects[0] ?? ruleStatus);
} else {
return transformAlertToRule(alert);
}
} else {
return createBulkErrorObject({
ruleId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { get } from 'lodash/fp';

import { SavedObject, SavedObjectAttributes, SavedObjectsFindResponse } from 'kibana/server';
import { SIGNALS_ID } from '../../../../common/constants';
import { AlertsClient } from '../../../../../alerting/server/alerts_client';
import { ActionsClient } from '../../../../../actions/server/actions_client';
Expand Down Expand Up @@ -49,10 +49,14 @@ export interface IRuleStatusAttributes {
status: RuleStatusString;
}

export interface IRuleSavedAttributesSavedObjectAttributes
extends IRuleStatusAttributes,
SavedObjectAttributes {}

export interface IRuleStatusSavedObject {
type: string;
id: string;
attributes: IRuleStatusAttributes;
attributes: Array<SavedObject<IRuleStatusAttributes & SavedObjectAttributes>>;
references: unknown[];
updated_at: string;
version: string;
Expand Down Expand Up @@ -145,14 +149,20 @@ export const isRuleStatusAttributes = (obj: unknown): obj is IRuleStatusAttribut
return get('lastSuccessMessage', obj) != null;
};

export const isRuleStatusSavedObjectType = (obj: unknown): obj is IRuleStatusSavedObject => {
export const isRuleStatusSavedObjectType = (
obj: unknown
): obj is SavedObject<IRuleSavedAttributesSavedObjectAttributes> => {
return get('attributes', obj) != null;
};

export const isRuleStatusFindType = (obj: unknown): obj is IRuleStatusFindType => {
export const isRuleStatusFindType = (
obj: unknown
): obj is SavedObjectsFindResponse<IRuleSavedAttributesSavedObjectAttributes> => {
return get('saved_objects', obj) != null;
};

export const isRuleStatusFindTypes = (obj: unknown[] | undefined): obj is IRuleStatusFindType[] => {
export const isRuleStatusFindTypes = (
obj: unknown[] | undefined
): obj is Array<SavedObjectsFindResponse<IRuleSavedAttributesSavedObjectAttributes>> => {
return obj ? obj.every(ruleStatus => isRuleStatusFindType(ruleStatus)) : false;
};
Loading

0 comments on commit 74a6fba

Please sign in to comment.