[EventLog] Added event log API to get events for multiple saved objects. (#87596)
* Added alerting API to get all active instances * modofied event log findEventsBySavedObject to support bulk ids, renamed to findEventsBySavedObjectIds * fixed faling typechecks * fixed crash on zpd/api/event_log/alert/84c00970-5130-11eb-9fa7/_find for non existing id * fixed faling typechecks * fixed faling typechecks * fixed due to comments * fixed due to comments * fixed failing test * fixed due to comments
This commit is contained in:
parent
5e4402c374
commit
fb67443e6d
|
@ -19,6 +19,7 @@ import {
|
||||||
ElasticsearchServiceStart,
|
ElasticsearchServiceStart,
|
||||||
ILegacyClusterClient,
|
ILegacyClusterClient,
|
||||||
SavedObjectsClientContract,
|
SavedObjectsClientContract,
|
||||||
|
SavedObjectsBulkGetObject,
|
||||||
} from '../../../../src/core/server';
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -333,7 +334,12 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
||||||
|
|
||||||
this.eventLogService!.registerSavedObjectProvider('action', (request) => {
|
this.eventLogService!.registerSavedObjectProvider('action', (request) => {
|
||||||
const client = secureGetActionsClientWithRequest(request);
|
const client = secureGetActionsClientWithRequest(request);
|
||||||
return async (type: string, id: string) => (await client).get({ id });
|
return (objects?: SavedObjectsBulkGetObject[]) =>
|
||||||
|
objects
|
||||||
|
? Promise.all(
|
||||||
|
objects.map(async (objectItem) => await (await client).get({ id: objectItem.id }))
|
||||||
|
)
|
||||||
|
: Promise.resolve([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
const getScopedSavedObjectsClientWithoutAccessToActions = (request: KibanaRequest) =>
|
const getScopedSavedObjectsClientWithoutAccessToActions = (request: KibanaRequest) =>
|
||||||
|
|
|
@ -412,7 +412,7 @@ export class AlertsClient {
|
||||||
this.logger.debug(`getAlertInstanceSummary(): search the event log for alert ${id}`);
|
this.logger.debug(`getAlertInstanceSummary(): search the event log for alert ${id}`);
|
||||||
let events: IEvent[];
|
let events: IEvent[];
|
||||||
try {
|
try {
|
||||||
const queryResults = await eventLogClient.findEventsBySavedObject('alert', id, {
|
const queryResults = await eventLogClient.findEventsBySavedObjectIds('alert', [id], {
|
||||||
page: 1,
|
page: 1,
|
||||||
per_page: 10000,
|
per_page: 10000,
|
||||||
start: parsedDateStart.toISOString(),
|
start: parsedDateStart.toISOString(),
|
||||||
|
|
|
@ -131,7 +131,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
total: events.length,
|
total: events.length,
|
||||||
data: events,
|
data: events,
|
||||||
};
|
};
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(eventsResult);
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(eventsResult);
|
||||||
|
|
||||||
const dateStart = new Date(Date.now() - 60 * 1000).toISOString();
|
const dateStart = new Date(Date.now() - 60 * 1000).toISOString();
|
||||||
|
|
||||||
|
@ -188,18 +188,20 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('calls saved objects and event log client with default params', async () => {
|
test('calls saved objects and event log client with default params', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(
|
||||||
AlertInstanceSummaryFindEventsResult
|
AlertInstanceSummaryFindEventsResult
|
||||||
);
|
);
|
||||||
|
|
||||||
await alertsClient.getAlertInstanceSummary({ id: '1' });
|
await alertsClient.getAlertInstanceSummary({ id: '1' });
|
||||||
|
|
||||||
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
||||||
expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
expect(eventLogClient.findEventsBySavedObject.mock.calls[0]).toMatchInlineSnapshot(`
|
expect(eventLogClient.findEventsBySavedObjectIds.mock.calls[0]).toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
"alert",
|
"alert",
|
||||||
"1",
|
Array [
|
||||||
|
"1",
|
||||||
|
],
|
||||||
Object {
|
Object {
|
||||||
"end": "2019-02-12T21:01:22.479Z",
|
"end": "2019-02-12T21:01:22.479Z",
|
||||||
"page": 1,
|
"page": 1,
|
||||||
|
@ -210,7 +212,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
// calculate the expected start/end date for one test
|
// calculate the expected start/end date for one test
|
||||||
const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
|
const { start, end } = eventLogClient.findEventsBySavedObjectIds.mock.calls[0][2]!;
|
||||||
expect(end).toBe(mockedDateString);
|
expect(end).toBe(mockedDateString);
|
||||||
|
|
||||||
const startMillis = Date.parse(start!);
|
const startMillis = Date.parse(start!);
|
||||||
|
@ -222,7 +224,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('calls event log client with start date', async () => {
|
test('calls event log client with start date', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(
|
||||||
AlertInstanceSummaryFindEventsResult
|
AlertInstanceSummaryFindEventsResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -232,8 +234,8 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
await alertsClient.getAlertInstanceSummary({ id: '1', dateStart });
|
await alertsClient.getAlertInstanceSummary({ id: '1', dateStart });
|
||||||
|
|
||||||
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
||||||
expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
|
const { start, end } = eventLogClient.findEventsBySavedObjectIds.mock.calls[0][2]!;
|
||||||
|
|
||||||
expect({ start, end }).toMatchInlineSnapshot(`
|
expect({ start, end }).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
|
@ -245,7 +247,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('calls event log client with relative start date', async () => {
|
test('calls event log client with relative start date', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(
|
||||||
AlertInstanceSummaryFindEventsResult
|
AlertInstanceSummaryFindEventsResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -253,8 +255,8 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
await alertsClient.getAlertInstanceSummary({ id: '1', dateStart });
|
await alertsClient.getAlertInstanceSummary({ id: '1', dateStart });
|
||||||
|
|
||||||
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
||||||
expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!;
|
const { start, end } = eventLogClient.findEventsBySavedObjectIds.mock.calls[0][2]!;
|
||||||
|
|
||||||
expect({ start, end }).toMatchInlineSnapshot(`
|
expect({ start, end }).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
|
@ -266,7 +268,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('invalid start date throws an error', async () => {
|
test('invalid start date throws an error', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(
|
||||||
AlertInstanceSummaryFindEventsResult
|
AlertInstanceSummaryFindEventsResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -280,7 +282,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('saved object get throws an error', async () => {
|
test('saved object get throws an error', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('OMG!'));
|
unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('OMG!'));
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(
|
||||||
AlertInstanceSummaryFindEventsResult
|
AlertInstanceSummaryFindEventsResult
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -291,7 +293,7 @@ describe('getAlertInstanceSummary()', () => {
|
||||||
|
|
||||||
test('findEvents throws an error', async () => {
|
test('findEvents throws an error', async () => {
|
||||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject());
|
||||||
eventLogClient.findEventsBySavedObject.mockRejectedValueOnce(new Error('OMG 2!'));
|
eventLogClient.findEventsBySavedObjectIds.mockRejectedValueOnce(new Error('OMG 2!'));
|
||||||
|
|
||||||
// error eaten but logged
|
// error eaten but logged
|
||||||
await alertsClient.getAlertInstanceSummary({ id: '1' });
|
await alertsClient.getAlertInstanceSummary({ id: '1' });
|
||||||
|
|
|
@ -33,6 +33,7 @@ import {
|
||||||
ILegacyClusterClient,
|
ILegacyClusterClient,
|
||||||
StatusServiceSetup,
|
StatusServiceSetup,
|
||||||
ServiceStatus,
|
ServiceStatus,
|
||||||
|
SavedObjectsBulkGetObject,
|
||||||
} from '../../../../src/core/server';
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -370,7 +371,10 @@ export class AlertingPlugin {
|
||||||
|
|
||||||
this.eventLogService!.registerSavedObjectProvider('alert', (request) => {
|
this.eventLogService!.registerSavedObjectProvider('alert', (request) => {
|
||||||
const client = getAlertsClientWithRequest(request);
|
const client = getAlertsClientWithRequest(request);
|
||||||
return (type: string, id: string) => client.get({ id });
|
return (objects?: SavedObjectsBulkGetObject[]) =>
|
||||||
|
objects
|
||||||
|
? Promise.all(objects.map(async (objectItem) => await client.get({ id: objectItem.id })))
|
||||||
|
: Promise.resolve([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
scheduleAlertingTelemetry(this.telemetryLogger, plugins.taskManager);
|
scheduleAlertingTelemetry(this.telemetryLogger, plugins.taskManager);
|
||||||
|
|
|
@ -16,7 +16,7 @@ const createClusterClientMock = () => {
|
||||||
createIndexTemplate: jest.fn(),
|
createIndexTemplate: jest.fn(),
|
||||||
doesAliasExist: jest.fn(),
|
doesAliasExist: jest.fn(),
|
||||||
createIndex: jest.fn(),
|
createIndex: jest.fn(),
|
||||||
queryEventsBySavedObject: jest.fn(),
|
queryEventsBySavedObjects: jest.fn(),
|
||||||
shutdown: jest.fn(),
|
shutdown: jest.fn(),
|
||||||
};
|
};
|
||||||
return mock;
|
return mock;
|
||||||
|
|
|
@ -327,11 +327,11 @@ describe('queryEventsBySavedObject', () => {
|
||||||
total: { value: 0 },
|
total: { value: 0 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await clusterClientAdapter.queryEventsBySavedObject(
|
await clusterClientAdapter.queryEventsBySavedObjects(
|
||||||
'index-name',
|
'index-name',
|
||||||
'namespace',
|
'namespace',
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
DEFAULT_OPTIONS
|
DEFAULT_OPTIONS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -365,10 +365,10 @@ describe('queryEventsBySavedObject', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"term": Object {
|
"terms": Object {
|
||||||
"kibana.saved_objects.id": Object {
|
"kibana.saved_objects.id": Array [
|
||||||
"value": "saved-object-id",
|
"saved-object-id",
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -406,11 +406,11 @@ describe('queryEventsBySavedObject', () => {
|
||||||
total: { value: 0 },
|
total: { value: 0 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await clusterClientAdapter.queryEventsBySavedObject(
|
await clusterClientAdapter.queryEventsBySavedObjects(
|
||||||
'index-name',
|
'index-name',
|
||||||
undefined,
|
undefined,
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
DEFAULT_OPTIONS
|
DEFAULT_OPTIONS
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -444,10 +444,10 @@ describe('queryEventsBySavedObject', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"term": Object {
|
"terms": Object {
|
||||||
"kibana.saved_objects.id": Object {
|
"kibana.saved_objects.id": Array [
|
||||||
"value": "saved-object-id",
|
"saved-object-id",
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -487,11 +487,11 @@ describe('queryEventsBySavedObject', () => {
|
||||||
total: { value: 0 },
|
total: { value: 0 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await clusterClientAdapter.queryEventsBySavedObject(
|
await clusterClientAdapter.queryEventsBySavedObjects(
|
||||||
'index-name',
|
'index-name',
|
||||||
'namespace',
|
'namespace',
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
{ ...DEFAULT_OPTIONS, sort_field: 'event.end', sort_order: 'desc' }
|
{ ...DEFAULT_OPTIONS, sort_field: 'event.end', sort_order: 'desc' }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -515,11 +515,11 @@ describe('queryEventsBySavedObject', () => {
|
||||||
|
|
||||||
const start = '2020-07-08T00:52:28.350Z';
|
const start = '2020-07-08T00:52:28.350Z';
|
||||||
|
|
||||||
await clusterClientAdapter.queryEventsBySavedObject(
|
await clusterClientAdapter.queryEventsBySavedObjects(
|
||||||
'index-name',
|
'index-name',
|
||||||
'namespace',
|
'namespace',
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
{ ...DEFAULT_OPTIONS, start }
|
{ ...DEFAULT_OPTIONS, start }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -553,10 +553,10 @@ describe('queryEventsBySavedObject', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"term": Object {
|
"terms": Object {
|
||||||
"kibana.saved_objects.id": Object {
|
"kibana.saved_objects.id": Array [
|
||||||
"value": "saved-object-id",
|
"saved-object-id",
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
@ -605,11 +605,11 @@ describe('queryEventsBySavedObject', () => {
|
||||||
const start = '2020-07-08T00:52:28.350Z';
|
const start = '2020-07-08T00:52:28.350Z';
|
||||||
const end = '2020-07-08T00:00:00.000Z';
|
const end = '2020-07-08T00:00:00.000Z';
|
||||||
|
|
||||||
await clusterClientAdapter.queryEventsBySavedObject(
|
await clusterClientAdapter.queryEventsBySavedObjects(
|
||||||
'index-name',
|
'index-name',
|
||||||
'namespace',
|
'namespace',
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
{ ...DEFAULT_OPTIONS, start, end }
|
{ ...DEFAULT_OPTIONS, start, end }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -643,10 +643,10 @@ describe('queryEventsBySavedObject', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"term": Object {
|
"terms": Object {
|
||||||
"kibana.saved_objects.id": Object {
|
"kibana.saved_objects.id": Array [
|
||||||
"value": "saved-object-id",
|
"saved-object-id",
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
|
|
|
@ -194,11 +194,11 @@ export class ClusterClientAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async queryEventsBySavedObject(
|
public async queryEventsBySavedObjects(
|
||||||
index: string,
|
index: string,
|
||||||
namespace: string | undefined,
|
namespace: string | undefined,
|
||||||
type: string,
|
type: string,
|
||||||
id: string,
|
ids: string[],
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
{ page, per_page: perPage, start, end, sort_field, sort_order }: FindOptionsType
|
{ page, per_page: perPage, start, end, sort_field, sort_order }: FindOptionsType
|
||||||
): Promise<QueryEventsBySavedObjectResult> {
|
): Promise<QueryEventsBySavedObjectResult> {
|
||||||
|
@ -249,10 +249,9 @@ export class ClusterClientAdapter {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
term: {
|
terms: {
|
||||||
'kibana.saved_objects.id': {
|
// default maximum of 65,536 terms, configurable by index.max_terms_count
|
||||||
value: id,
|
'kibana.saved_objects.id': ids,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
namespaceQuery,
|
namespaceQuery,
|
||||||
|
@ -298,7 +297,7 @@ export class ClusterClientAdapter {
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`querying for Event Log by for type "${type}" and id "${id}" failed with: ${err.message}`
|
`querying for Event Log by for type "${type}" and ids "${ids}" failed with: ${err.message}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { IEventLogClient } from './types';
|
||||||
|
|
||||||
const createEventLogClientMock = () => {
|
const createEventLogClientMock = () => {
|
||||||
const mock: jest.Mocked<IEventLogClient> = {
|
const mock: jest.Mocked<IEventLogClient> = {
|
||||||
findEventsBySavedObject: jest.fn(),
|
findEventsBySavedObjectIds: jest.fn(),
|
||||||
};
|
};
|
||||||
return mock;
|
return mock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { merge } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
describe('EventLogStart', () => {
|
describe('EventLogStart', () => {
|
||||||
describe('findEventsBySavedObject', () => {
|
describe('findEventsBySavedObjectIds', () => {
|
||||||
test('verifies that the user can access the specified saved object', async () => {
|
test('verifies that the user can access the specified saved object', async () => {
|
||||||
const esContext = contextMock.create();
|
const esContext = contextMock.create();
|
||||||
const savedObjectGetter = jest.fn();
|
const savedObjectGetter = jest.fn();
|
||||||
|
@ -29,9 +29,9 @@ describe('EventLogStart', () => {
|
||||||
references: [],
|
references: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id');
|
await eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id']);
|
||||||
|
|
||||||
expect(savedObjectGetter).toHaveBeenCalledWith('saved-object-type', 'saved-object-id');
|
expect(savedObjectGetter).toHaveBeenCalledWith('saved-object-type', ['saved-object-id']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws when the user doesnt have permission to access the specified saved object', async () => {
|
test('throws when the user doesnt have permission to access the specified saved object', async () => {
|
||||||
|
@ -48,7 +48,7 @@ describe('EventLogStart', () => {
|
||||||
savedObjectGetter.mockRejectedValue(new Error('Fail'));
|
savedObjectGetter.mockRejectedValue(new Error('Fail'));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id')
|
eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id'])
|
||||||
).rejects.toMatchInlineSnapshot(`[Error: Fail]`);
|
).rejects.toMatchInlineSnapshot(`[Error: Fail]`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -107,17 +107,17 @@ describe('EventLogStart', () => {
|
||||||
total: expectedEvents.length,
|
total: expectedEvents.length,
|
||||||
data: expectedEvents,
|
data: expectedEvents,
|
||||||
};
|
};
|
||||||
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result);
|
esContext.esAdapter.queryEventsBySavedObjects.mockResolvedValue(result);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id')
|
await eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id'])
|
||||||
).toEqual(result);
|
).toEqual(result);
|
||||||
|
|
||||||
expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith(
|
expect(esContext.esAdapter.queryEventsBySavedObjects).toHaveBeenCalledWith(
|
||||||
esContext.esNames.indexPattern,
|
esContext.esNames.indexPattern,
|
||||||
undefined,
|
undefined,
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
{
|
{
|
||||||
page: 1,
|
page: 1,
|
||||||
per_page: 10,
|
per_page: 10,
|
||||||
|
@ -182,23 +182,23 @@ describe('EventLogStart', () => {
|
||||||
total: expectedEvents.length,
|
total: expectedEvents.length,
|
||||||
data: expectedEvents,
|
data: expectedEvents,
|
||||||
};
|
};
|
||||||
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result);
|
esContext.esAdapter.queryEventsBySavedObjects.mockResolvedValue(result);
|
||||||
|
|
||||||
const start = moment().subtract(1, 'days').toISOString();
|
const start = moment().subtract(1, 'days').toISOString();
|
||||||
const end = moment().add(1, 'days').toISOString();
|
const end = moment().add(1, 'days').toISOString();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', {
|
await eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id'], {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
).toEqual(result);
|
).toEqual(result);
|
||||||
|
|
||||||
expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith(
|
expect(esContext.esAdapter.queryEventsBySavedObjects).toHaveBeenCalledWith(
|
||||||
esContext.esNames.indexPattern,
|
esContext.esNames.indexPattern,
|
||||||
undefined,
|
undefined,
|
||||||
'saved-object-type',
|
'saved-object-type',
|
||||||
'saved-object-id',
|
['saved-object-id'],
|
||||||
{
|
{
|
||||||
page: 1,
|
page: 1,
|
||||||
per_page: 10,
|
per_page: 10,
|
||||||
|
@ -228,7 +228,7 @@ describe('EventLogStart', () => {
|
||||||
references: [],
|
references: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({
|
esContext.esAdapter.queryEventsBySavedObjects.mockResolvedValue({
|
||||||
page: 0,
|
page: 0,
|
||||||
per_page: 0,
|
per_page: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
@ -236,7 +236,7 @@ describe('EventLogStart', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', {
|
eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id'], {
|
||||||
start: 'not a date string',
|
start: 'not a date string',
|
||||||
})
|
})
|
||||||
).rejects.toMatchInlineSnapshot(`[Error: [start]: Invalid Date]`);
|
).rejects.toMatchInlineSnapshot(`[Error: [start]: Invalid Date]`);
|
||||||
|
@ -260,7 +260,7 @@ describe('EventLogStart', () => {
|
||||||
references: [],
|
references: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({
|
esContext.esAdapter.queryEventsBySavedObjects.mockResolvedValue({
|
||||||
page: 0,
|
page: 0,
|
||||||
per_page: 0,
|
per_page: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
@ -268,7 +268,7 @@ describe('EventLogStart', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', {
|
eventLogClient.findEventsBySavedObjectIds('saved-object-type', ['saved-object-id'], {
|
||||||
end: 'not a date string',
|
end: 'not a date string',
|
||||||
})
|
})
|
||||||
).rejects.toMatchInlineSnapshot(`[Error: [end]: Invalid Date]`);
|
).rejects.toMatchInlineSnapshot(`[Error: [end]: Invalid Date]`);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { SpacesServiceStart } from '../../spaces/server';
|
||||||
import { EsContext } from './es';
|
import { EsContext } from './es';
|
||||||
import { IEventLogClient } from './types';
|
import { IEventLogClient } from './types';
|
||||||
import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter';
|
import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter';
|
||||||
import { SavedObjectGetter } from './saved_object_provider_registry';
|
import { SavedObjectBulkGetterResult } from './saved_object_provider_registry';
|
||||||
export type PluginClusterClient = Pick<LegacyClusterClient, 'callAsInternalUser' | 'asScoped'>;
|
export type PluginClusterClient = Pick<LegacyClusterClient, 'callAsInternalUser' | 'asScoped'>;
|
||||||
export type AdminClusterClient$ = Observable<PluginClusterClient>;
|
export type AdminClusterClient$ = Observable<PluginClusterClient>;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ export type FindOptionsType = Pick<
|
||||||
|
|
||||||
interface EventLogServiceCtorParams {
|
interface EventLogServiceCtorParams {
|
||||||
esContext: EsContext;
|
esContext: EsContext;
|
||||||
savedObjectGetter: SavedObjectGetter;
|
savedObjectGetter: SavedObjectBulkGetterResult;
|
||||||
spacesService?: SpacesServiceStart;
|
spacesService?: SpacesServiceStart;
|
||||||
request: KibanaRequest;
|
request: KibanaRequest;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ interface EventLogServiceCtorParams {
|
||||||
// note that clusterClient may be null, indicating we can't write to ES
|
// note that clusterClient may be null, indicating we can't write to ES
|
||||||
export class EventLogClient implements IEventLogClient {
|
export class EventLogClient implements IEventLogClient {
|
||||||
private esContext: EsContext;
|
private esContext: EsContext;
|
||||||
private savedObjectGetter: SavedObjectGetter;
|
private savedObjectGetter: SavedObjectBulkGetterResult;
|
||||||
private spacesService?: SpacesServiceStart;
|
private spacesService?: SpacesServiceStart;
|
||||||
private request: KibanaRequest;
|
private request: KibanaRequest;
|
||||||
|
|
||||||
|
@ -78,9 +78,9 @@ export class EventLogClient implements IEventLogClient {
|
||||||
this.request = request;
|
this.request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findEventsBySavedObject(
|
async findEventsBySavedObjectIds(
|
||||||
type: string,
|
type: string,
|
||||||
id: string,
|
ids: string[],
|
||||||
options?: Partial<FindOptionsType>
|
options?: Partial<FindOptionsType>
|
||||||
): Promise<QueryEventsBySavedObjectResult> {
|
): Promise<QueryEventsBySavedObjectResult> {
|
||||||
const findOptions = findOptionsSchema.validate(options ?? {});
|
const findOptions = findOptionsSchema.validate(options ?? {});
|
||||||
|
@ -88,14 +88,14 @@ export class EventLogClient implements IEventLogClient {
|
||||||
const space = await this.spacesService?.getActiveSpace(this.request);
|
const space = await this.spacesService?.getActiveSpace(this.request);
|
||||||
const namespace = space && this.spacesService?.spaceIdToNamespace(space.id);
|
const namespace = space && this.spacesService?.spaceIdToNamespace(space.id);
|
||||||
|
|
||||||
// verify the user has the required permissions to view this saved object
|
// verify the user has the required permissions to view this saved objects
|
||||||
await this.savedObjectGetter(type, id);
|
await this.savedObjectGetter(type, ids);
|
||||||
|
|
||||||
return await this.esContext.esAdapter.queryEventsBySavedObject(
|
return await this.esContext.esAdapter.queryEventsBySavedObjects(
|
||||||
this.esContext.esNames.indexPattern,
|
this.esContext.esNames.indexPattern,
|
||||||
namespace,
|
namespace,
|
||||||
type,
|
type,
|
||||||
id,
|
ids,
|
||||||
findOptions
|
findOptions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { EventLogService } from './event_log_service';
|
||||||
import { createEsContext, EsContext } from './es';
|
import { createEsContext, EsContext } from './es';
|
||||||
import { EventLogClientService } from './event_log_start_service';
|
import { EventLogClientService } from './event_log_start_service';
|
||||||
import { SavedObjectProviderRegistry } from './saved_object_provider_registry';
|
import { SavedObjectProviderRegistry } from './saved_object_provider_registry';
|
||||||
|
import { findByIdsRoute } from './routes/find_by_ids';
|
||||||
|
|
||||||
export type PluginClusterClient = Pick<LegacyClusterClient, 'callAsInternalUser' | 'asScoped'>;
|
export type PluginClusterClient = Pick<LegacyClusterClient, 'callAsInternalUser' | 'asScoped'>;
|
||||||
|
|
||||||
|
@ -99,6 +100,7 @@ export class Plugin implements CorePlugin<IEventLogService, IEventLogClientServi
|
||||||
const router = core.http.createRouter();
|
const router = core.http.createRouter();
|
||||||
// Register routes
|
// Register routes
|
||||||
findRoute(router, this.systemLogger);
|
findRoute(router, this.systemLogger);
|
||||||
|
findByIdsRoute(router, this.systemLogger);
|
||||||
|
|
||||||
return this.eventLogService;
|
return this.eventLogService;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +137,7 @@ export class Plugin implements CorePlugin<IEventLogService, IEventLogClientServi
|
||||||
|
|
||||||
this.savedObjectProviderRegistry.registerDefaultProvider((request) => {
|
this.savedObjectProviderRegistry.registerDefaultProvider((request) => {
|
||||||
const client = core.savedObjects.getScopedClient(request);
|
const client = core.savedObjects.getScopedClient(request);
|
||||||
return client.get.bind(client);
|
return client.bulkGet.bind(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.eventLogClientService = new EventLogClientService({
|
this.eventLogClientService = new EventLogClientService({
|
||||||
|
|
|
@ -34,7 +34,7 @@ describe('find', () => {
|
||||||
total: events.length,
|
total: events.length,
|
||||||
data: events,
|
data: events,
|
||||||
};
|
};
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(result);
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(result);
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
eventLogClient,
|
eventLogClient,
|
||||||
|
@ -46,11 +46,11 @@ describe('find', () => {
|
||||||
|
|
||||||
await handler(context, req, res);
|
await handler(context, req, res);
|
||||||
|
|
||||||
expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const [type, id] = eventLogClient.findEventsBySavedObject.mock.calls[0];
|
const [type, ids] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0];
|
||||||
expect(type).toEqual(`action`);
|
expect(type).toEqual(`action`);
|
||||||
expect(id).toEqual(`1`);
|
expect(ids).toEqual(['1']);
|
||||||
|
|
||||||
expect(res.ok).toHaveBeenCalledWith({
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
body: result,
|
body: result,
|
||||||
|
@ -63,7 +63,7 @@ describe('find', () => {
|
||||||
findRoute(router, systemLogger);
|
findRoute(router, systemLogger);
|
||||||
|
|
||||||
const [, handler] = router.get.mock.calls[0];
|
const [, handler] = router.get.mock.calls[0];
|
||||||
eventLogClient.findEventsBySavedObject.mockResolvedValueOnce({
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce({
|
||||||
page: 0,
|
page: 0,
|
||||||
per_page: 10,
|
per_page: 10,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
@ -81,11 +81,11 @@ describe('find', () => {
|
||||||
|
|
||||||
await handler(context, req, res);
|
await handler(context, req, res);
|
||||||
|
|
||||||
expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1);
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
const [type, id, options] = eventLogClient.findEventsBySavedObject.mock.calls[0];
|
const [type, ids, options] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0];
|
||||||
expect(type).toEqual(`action`);
|
expect(type).toEqual(`action`);
|
||||||
expect(id).toEqual(`1`);
|
expect(ids).toEqual(['1']);
|
||||||
expect(options).toMatchObject({});
|
expect(options).toMatchObject({});
|
||||||
|
|
||||||
expect(res.ok).toHaveBeenCalledWith({
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
|
@ -104,7 +104,7 @@ describe('find', () => {
|
||||||
findRoute(router, systemLogger);
|
findRoute(router, systemLogger);
|
||||||
|
|
||||||
const [, handler] = router.get.mock.calls[0];
|
const [, handler] = router.get.mock.calls[0];
|
||||||
eventLogClient.findEventsBySavedObject.mockRejectedValueOnce(new Error('oof!'));
|
eventLogClient.findEventsBySavedObjectIds.mockRejectedValueOnce(new Error('oof!'));
|
||||||
|
|
||||||
const [context, req, res] = mockHandlerArguments(
|
const [context, req, res] = mockHandlerArguments(
|
||||||
eventLogClient,
|
eventLogClient,
|
||||||
|
@ -119,7 +119,7 @@ describe('find', () => {
|
||||||
|
|
||||||
expect(systemLogger.debug).toHaveBeenCalledTimes(1);
|
expect(systemLogger.debug).toHaveBeenCalledTimes(1);
|
||||||
expect(systemLogger.debug).toHaveBeenCalledWith(
|
expect(systemLogger.debug).toHaveBeenCalledWith(
|
||||||
'error calling eventLog findEventsBySavedObject(action, 1, {"page":3,"per_page":10}): oof!'
|
'error calling eventLog findEventsBySavedObjectIds(action, [1], {"page":3,"per_page":10}): oof!'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,10 +47,10 @@ export const findRoute = (router: IRouter, systemLogger: Logger) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return res.ok({
|
return res.ok({
|
||||||
body: await eventLogClient.findEventsBySavedObject(type, id, query),
|
body: await eventLogClient.findEventsBySavedObjectIds(type, [id], query),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const call = `findEventsBySavedObject(${type}, ${id}, ${JSON.stringify(query)})`;
|
const call = `findEventsBySavedObjectIds(${type}, [${id}], ${JSON.stringify(query)})`;
|
||||||
systemLogger.debug(`error calling eventLog ${call}: ${err.message}`);
|
systemLogger.debug(`error calling eventLog ${call}: ${err.message}`);
|
||||||
return res.notFound();
|
return res.notFound();
|
||||||
}
|
}
|
||||||
|
|
128
x-pack/plugins/event_log/server/routes/find_by_ids.test.ts
Normal file
128
x-pack/plugins/event_log/server/routes/find_by_ids.test.ts
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { httpServiceMock } from 'src/core/server/mocks';
|
||||||
|
import { mockHandlerArguments, fakeEvent } from './_mock_handler_arguments';
|
||||||
|
import { eventLogClientMock } from '../event_log_client.mock';
|
||||||
|
import { loggingSystemMock } from 'src/core/server/mocks';
|
||||||
|
import { findByIdsRoute } from './find_by_ids';
|
||||||
|
|
||||||
|
const eventLogClient = eventLogClientMock.create();
|
||||||
|
const systemLogger = loggingSystemMock.createLogger();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('find_by_ids', () => {
|
||||||
|
it('finds events with proper parameters', async () => {
|
||||||
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
|
findByIdsRoute(router, systemLogger);
|
||||||
|
|
||||||
|
const [config, handler] = router.post.mock.calls[0];
|
||||||
|
|
||||||
|
expect(config.path).toMatchInlineSnapshot(`"/api/event_log/{type}/_find"`);
|
||||||
|
|
||||||
|
const events = [fakeEvent(), fakeEvent()];
|
||||||
|
const result = {
|
||||||
|
page: 0,
|
||||||
|
per_page: 10,
|
||||||
|
total: events.length,
|
||||||
|
data: events,
|
||||||
|
};
|
||||||
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce(result);
|
||||||
|
|
||||||
|
const [context, req, res] = mockHandlerArguments(
|
||||||
|
eventLogClient,
|
||||||
|
{
|
||||||
|
params: { type: 'action' },
|
||||||
|
body: { ids: ['1'] },
|
||||||
|
},
|
||||||
|
['ok']
|
||||||
|
);
|
||||||
|
|
||||||
|
await handler(context, req, res);
|
||||||
|
|
||||||
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const [type, ids] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0];
|
||||||
|
expect(type).toEqual(`action`);
|
||||||
|
expect(ids).toEqual(['1']);
|
||||||
|
|
||||||
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
|
body: result,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('supports optional pagination parameters', async () => {
|
||||||
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
|
findByIdsRoute(router, systemLogger);
|
||||||
|
|
||||||
|
const [, handler] = router.post.mock.calls[0];
|
||||||
|
eventLogClient.findEventsBySavedObjectIds.mockResolvedValueOnce({
|
||||||
|
page: 0,
|
||||||
|
per_page: 10,
|
||||||
|
total: 0,
|
||||||
|
data: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const [context, req, res] = mockHandlerArguments(
|
||||||
|
eventLogClient,
|
||||||
|
{
|
||||||
|
params: { type: 'action' },
|
||||||
|
body: { ids: ['1'] },
|
||||||
|
query: { page: 3, per_page: 10 },
|
||||||
|
},
|
||||||
|
['ok']
|
||||||
|
);
|
||||||
|
|
||||||
|
await handler(context, req, res);
|
||||||
|
|
||||||
|
expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const [type, id, options] = eventLogClient.findEventsBySavedObjectIds.mock.calls[0];
|
||||||
|
expect(type).toEqual(`action`);
|
||||||
|
expect(id).toEqual(['1']);
|
||||||
|
expect(options).toMatchObject({});
|
||||||
|
|
||||||
|
expect(res.ok).toHaveBeenCalledWith({
|
||||||
|
body: {
|
||||||
|
page: 0,
|
||||||
|
per_page: 10,
|
||||||
|
total: 0,
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs a warning when the query throws an error', async () => {
|
||||||
|
const router = httpServiceMock.createRouter();
|
||||||
|
|
||||||
|
findByIdsRoute(router, systemLogger);
|
||||||
|
|
||||||
|
const [, handler] = router.post.mock.calls[0];
|
||||||
|
eventLogClient.findEventsBySavedObjectIds.mockRejectedValueOnce(new Error('oof!'));
|
||||||
|
|
||||||
|
const [context, req, res] = mockHandlerArguments(
|
||||||
|
eventLogClient,
|
||||||
|
{
|
||||||
|
params: { type: 'action' },
|
||||||
|
body: { ids: ['1'] },
|
||||||
|
query: { page: 3, per_page: 10 },
|
||||||
|
},
|
||||||
|
['ok']
|
||||||
|
);
|
||||||
|
|
||||||
|
await handler(context, req, res);
|
||||||
|
|
||||||
|
expect(systemLogger.debug).toHaveBeenCalledTimes(1);
|
||||||
|
expect(systemLogger.debug).toHaveBeenCalledWith(
|
||||||
|
'error calling eventLog findEventsBySavedObjectIds(action, [1], {"page":3,"per_page":10}): oof!'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
64
x-pack/plugins/event_log/server/routes/find_by_ids.ts
Normal file
64
x-pack/plugins/event_log/server/routes/find_by_ids.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
import {
|
||||||
|
IRouter,
|
||||||
|
RequestHandlerContext,
|
||||||
|
KibanaRequest,
|
||||||
|
IKibanaResponse,
|
||||||
|
KibanaResponseFactory,
|
||||||
|
Logger,
|
||||||
|
} from 'src/core/server';
|
||||||
|
|
||||||
|
import { BASE_EVENT_LOG_API_PATH } from '../../common';
|
||||||
|
import { findOptionsSchema, FindOptionsType } from '../event_log_client';
|
||||||
|
|
||||||
|
const paramSchema = schema.object({
|
||||||
|
type: schema.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const bodySchema = schema.object({
|
||||||
|
ids: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const findByIdsRoute = (router: IRouter, systemLogger: Logger) => {
|
||||||
|
router.post(
|
||||||
|
{
|
||||||
|
path: `${BASE_EVENT_LOG_API_PATH}/{type}/_find`,
|
||||||
|
validate: {
|
||||||
|
params: paramSchema,
|
||||||
|
query: findOptionsSchema,
|
||||||
|
body: bodySchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
router.handleLegacyErrors(async function (
|
||||||
|
context: RequestHandlerContext,
|
||||||
|
req: KibanaRequest<TypeOf<typeof paramSchema>, FindOptionsType, TypeOf<typeof bodySchema>>,
|
||||||
|
res: KibanaResponseFactory
|
||||||
|
): Promise<IKibanaResponse> {
|
||||||
|
if (!context.eventLog) {
|
||||||
|
return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' });
|
||||||
|
}
|
||||||
|
const eventLogClient = context.eventLog.getEventLogClient();
|
||||||
|
const {
|
||||||
|
params: { type },
|
||||||
|
body: { ids },
|
||||||
|
query,
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return res.ok({
|
||||||
|
body: await eventLogClient.findEventsBySavedObjectIds(type, ids, query),
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const call = `findEventsBySavedObjectIds(${type}, [${ids}], ${JSON.stringify(query)})`;
|
||||||
|
systemLogger.debug(`error calling eventLog ${call}: ${err.message}`);
|
||||||
|
return res.notFound();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
|
@ -45,10 +45,10 @@ describe('SavedObjectProviderRegistry', () => {
|
||||||
|
|
||||||
getter.mockResolvedValue(alert);
|
getter.mockResolvedValue(alert);
|
||||||
|
|
||||||
expect(await registry.getProvidersClient(request)('alert', alert.id)).toMatchObject(alert);
|
expect(await registry.getProvidersClient(request)('alert', [alert.id])).toMatchObject(alert);
|
||||||
|
|
||||||
expect(provider).toHaveBeenCalledWith(request);
|
expect(provider).toHaveBeenCalledWith(request);
|
||||||
expect(getter).toHaveBeenCalledWith('alert', alert.id);
|
expect(getter).toHaveBeenCalledWith([{ id: alert.id, type: 'alert' }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should get SavedObject using the default provider for unregistered types', async () => {
|
test('should get SavedObject using the default provider for unregistered types', async () => {
|
||||||
|
@ -70,9 +70,11 @@ describe('SavedObjectProviderRegistry', () => {
|
||||||
defaultProvider.mockReturnValue(getter);
|
defaultProvider.mockReturnValue(getter);
|
||||||
getter.mockResolvedValue(action);
|
getter.mockResolvedValue(action);
|
||||||
|
|
||||||
expect(await registry.getProvidersClient(request)('action', action.id)).toMatchObject(action);
|
expect(await registry.getProvidersClient(request)('action', [action.id])).toMatchObject(
|
||||||
|
action
|
||||||
|
);
|
||||||
|
|
||||||
expect(getter).toHaveBeenCalledWith('action', action.id);
|
expect(getter).toHaveBeenCalledWith([{ id: action.id, type: 'action' }]);
|
||||||
expect(defaultProvider).toHaveBeenCalledWith(request);
|
expect(defaultProvider).toHaveBeenCalledWith(request);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,14 @@ import { pipe } from 'fp-ts/lib/pipeable';
|
||||||
export type SavedObjectGetter = (
|
export type SavedObjectGetter = (
|
||||||
...params: Parameters<SavedObjectsClientContract['get']>
|
...params: Parameters<SavedObjectsClientContract['get']>
|
||||||
) => Promise<unknown>;
|
) => Promise<unknown>;
|
||||||
export type SavedObjectProvider = (request: KibanaRequest) => SavedObjectGetter;
|
|
||||||
|
export type SavedObjectBulkGetter = (
|
||||||
|
...params: Parameters<SavedObjectsClientContract['bulkGet']>
|
||||||
|
) => Promise<unknown>;
|
||||||
|
|
||||||
|
export type SavedObjectBulkGetterResult = (type: string, ids: string[]) => Promise<unknown>;
|
||||||
|
|
||||||
|
export type SavedObjectProvider = (request: KibanaRequest) => SavedObjectBulkGetter;
|
||||||
|
|
||||||
export class SavedObjectProviderRegistry {
|
export class SavedObjectProviderRegistry {
|
||||||
private providers = new Map<string, SavedObjectProvider>();
|
private providers = new Map<string, SavedObjectProvider>();
|
||||||
|
@ -34,7 +41,7 @@ export class SavedObjectProviderRegistry {
|
||||||
this.providers.set(type, provider);
|
this.providers.set(type, provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getProvidersClient(request: KibanaRequest): SavedObjectGetter {
|
public getProvidersClient(request: KibanaRequest): SavedObjectBulkGetterResult {
|
||||||
if (!this.defaultProvider) {
|
if (!this.defaultProvider) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
i18n.translate(
|
i18n.translate(
|
||||||
|
@ -49,9 +56,13 @@ export class SavedObjectProviderRegistry {
|
||||||
// `scopedProviders` is a cache of providers which are scoped t othe current request.
|
// `scopedProviders` is a cache of providers which are scoped t othe current request.
|
||||||
// The client will only instantiate a provider on-demand and it will cache each
|
// The client will only instantiate a provider on-demand and it will cache each
|
||||||
// one to enable the request to reuse each provider.
|
// one to enable the request to reuse each provider.
|
||||||
const scopedProviders = new Map<string, SavedObjectGetter>();
|
|
||||||
|
// would be nice to have a simple version support in API:
|
||||||
|
// curl -X GET "localhost:9200/my-index-000001/_mget?pretty" -H 'Content-Type: application/json' -d' { "ids" : ["1", "2"] } '
|
||||||
|
const scopedProviders = new Map<string, SavedObjectBulkGetter>();
|
||||||
const defaultGetter = this.defaultProvider(request);
|
const defaultGetter = this.defaultProvider(request);
|
||||||
return (type: string, id: string) => {
|
return (type: string, ids: string[]) => {
|
||||||
|
const objects = ids.map((id: string) => ({ type, id }));
|
||||||
const getter = pipe(
|
const getter = pipe(
|
||||||
fromNullable(scopedProviders.get(type)),
|
fromNullable(scopedProviders.get(type)),
|
||||||
getOrElse(() => {
|
getOrElse(() => {
|
||||||
|
@ -62,7 +73,7 @@ export class SavedObjectProviderRegistry {
|
||||||
return client;
|
return client;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return getter(type, id);
|
return getter(objects);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,9 @@ export interface IEventLogClientService {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEventLogClient {
|
export interface IEventLogClient {
|
||||||
findEventsBySavedObject(
|
findEventsBySavedObjectIds(
|
||||||
type: string,
|
type: string,
|
||||||
id: string,
|
ids: string[],
|
||||||
options?: Partial<FindOptionsType>
|
options?: Partial<FindOptionsType>
|
||||||
): Promise<QueryEventsBySavedObjectResult>;
|
): Promise<QueryEventsBySavedObjectResult>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue