Skip to content

Commit

Permalink
added sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
gmmorris committed Apr 3, 2020
1 parent b6ac43a commit 2bbac1a
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 95 deletions.
58 changes: 47 additions & 11 deletions x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ClusterClient, Logger } from '../../../../../src/core/server';
import { elasticsearchServiceMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
import { ClusterClientAdapter, IClusterClientAdapter } from './cluster_client_adapter';
import moment from 'moment';
import { findOptionsSchema } from '../event_log_client';

type EsClusterClient = Pick<jest.Mocked<ClusterClient>, 'callAsInternalUser' | 'asScoped'>;

Expand Down Expand Up @@ -198,23 +199,30 @@ describe('createIndex', () => {
});

describe('queryEventsBySavedObject', () => {
const DEFAULT_OPTIONS = findOptionsSchema.validate({});

test('should call cluster with proper arguments', async () => {
clusterClient.callAsInternalUser.mockResolvedValue({
hits: {
hits: [],
total: { value: 0 },
},
});
await clusterClientAdapter.queryEventsBySavedObject(
'index-name',
'saved-object-type',
'saved-object-id',
{ page: 10, per_page: 10, start: undefined, end: undefined }
DEFAULT_OPTIONS
);
expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('search', {

const [method, query] = clusterClient.callAsInternalUser.mock.calls[0];
expect(method).toEqual('search');
expect(query).toMatchObject({
index: 'index-name',
body: {
from: 90,
from: 0,
size: 10,
sort: { 'event.start': { order: 'asc' } },
query: {
bool: {
must: [
Expand All @@ -231,10 +239,35 @@ describe('queryEventsBySavedObject', () => {
});
});

test('should call cluster with sort', async () => {
clusterClient.callAsInternalUser.mockResolvedValue({
hits: {
hits: [],
total: { value: 0 },
},
});
await clusterClientAdapter.queryEventsBySavedObject(
'index-name',
'saved-object-type',
'saved-object-id',
{ ...DEFAULT_OPTIONS, sort_field: 'event.end', sort_order: 'desc' }
);

const [method, query] = clusterClient.callAsInternalUser.mock.calls[0];
expect(method).toEqual('search');
expect(query).toMatchObject({
index: 'index-name',
body: {
sort: { 'event.end': { order: 'desc' } },
},
});
});

test('supports open ended date', async () => {
clusterClient.callAsInternalUser.mockResolvedValue({
hits: {
hits: [],
total: { value: 0 },
},
});

Expand All @@ -246,13 +279,14 @@ describe('queryEventsBySavedObject', () => {
'index-name',
'saved-object-type',
'saved-object-id',
{ page: 10, per_page: 10, start }
{ ...DEFAULT_OPTIONS, start }
);
expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('search', {

const [method, query] = clusterClient.callAsInternalUser.mock.calls[0];
expect(method).toEqual('search');
expect(query).toMatchObject({
index: 'index-name',
body: {
from: 90,
size: 10,
query: {
bool: {
must: [
Expand Down Expand Up @@ -280,6 +314,7 @@ describe('queryEventsBySavedObject', () => {
clusterClient.callAsInternalUser.mockResolvedValue({
hits: {
hits: [],
total: { value: 0 },
},
});

Expand All @@ -294,13 +329,14 @@ describe('queryEventsBySavedObject', () => {
'index-name',
'saved-object-type',
'saved-object-id',
{ page: 10, per_page: 10, start, end }
{ ...DEFAULT_OPTIONS, start, end }
);
expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('search', {

const [method, query] = clusterClient.callAsInternalUser.mock.calls[0];
expect(method).toEqual('search');
expect(query).toMatchObject({
index: 'index-name',
body: {
from: 90,
size: 10,
query: {
bool: {
must: [
Expand Down
32 changes: 22 additions & 10 deletions x-pack/plugins/event_log/server/es/cluster_client_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ export interface ConstructorOpts {
clusterClient: EsClusterClient;
}

export interface QueryEventsBySavedObjectResult {
page: number;
per_page: number;
total: number;
data: IEvent[];
}

export class ClusterClientAdapter {
private readonly logger: Logger;
private readonly clusterClient: EsClusterClient;
Expand Down Expand Up @@ -114,20 +121,20 @@ export class ClusterClientAdapter {
index: string,
type: string,
id: string,
{ page, per_page: size, start, end }: Partial<FindOptionsType>
): Promise<any[]> {
{ page, per_page: perPage, start, end, sort_field, sort_order }: FindOptionsType
): Promise<QueryEventsBySavedObjectResult> {
try {
const {
hits: { hits },
hits: {
hits,
total: { value: total },
},
} = await this.callEs('search', {
index,
body: {
...(size && page
? {
size,
from: (page - 1) * size,
}
: {}),
size: perPage,
from: (page - 1) * perPage,
sort: { [sort_field]: { order: sort_order } },
query: {
bool: {
must: reject(
Expand Down Expand Up @@ -159,7 +166,12 @@ export class ClusterClientAdapter {
},
},
});
return hits.map((hit: any) => hit._source) as IEvent[];
return {
page,
per_page: perPage,
total,
data: hits.map((hit: any) => hit._source) as IEvent[],
};
} catch (err) {
throw new Error(
`querying for Event Log by for type "${type}" and id "${id}" failed with: ${err.message}`
Expand Down
38 changes: 32 additions & 6 deletions x-pack/plugins/event_log/server/event_log_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,17 @@ describe('EventLogStart', () => {
}),
];

esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(expectedEvents);
const result = {
page: 0,
per_page: 10,
total: expectedEvents.length,
data: expectedEvents,
};
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result);

expect(
await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id')
).toEqual(expectedEvents);
).toEqual(result);

expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith(
esContext.esNames.alias,
Expand All @@ -106,6 +112,8 @@ describe('EventLogStart', () => {
{
page: 1,
per_page: 10,
sort_field: 'event.start',
sort_order: 'asc',
}
);
});
Expand Down Expand Up @@ -156,7 +164,13 @@ describe('EventLogStart', () => {
}),
];

esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(expectedEvents);
const result = {
page: 0,
per_page: 10,
total: expectedEvents.length,
data: expectedEvents,
};
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result);

const start = moment()
.subtract(1, 'days')
Expand All @@ -170,7 +184,7 @@ describe('EventLogStart', () => {
start,
end,
})
).toEqual(expectedEvents);
).toEqual(result);

expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith(
esContext.esNames.alias,
Expand All @@ -179,6 +193,8 @@ describe('EventLogStart', () => {
{
page: 1,
per_page: 10,
sort_field: 'event.start',
sort_order: 'asc',
start,
end,
}
Expand All @@ -200,7 +216,12 @@ describe('EventLogStart', () => {
references: [],
});

esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue([]);
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({
page: 0,
per_page: 0,
total: 0,
data: [],
});

expect(
eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', {
Expand All @@ -224,7 +245,12 @@ describe('EventLogStart', () => {
references: [],
});

esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue([]);
esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({
page: 0,
per_page: 0,
total: 0,
data: [],
});

expect(
eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', {
Expand Down
34 changes: 29 additions & 5 deletions x-pack/plugins/event_log/server/event_log_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { ClusterClient, SavedObjectsClientContract } from 'src/core/server';

import { schema, TypeOf } from '@kbn/config-schema';
import { EsContext } from './es';
import { IEventLogClient, IEvent } from './types';
import { IEventLogClient } from './types';
import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter';
export type PluginClusterClient = Pick<ClusterClient, 'callAsInternalUser' | 'asScoped'>;
export type AdminClusterClient$ = Observable<PluginClusterClient>;

Expand All @@ -33,8 +34,30 @@ export const findOptionsSchema = schema.object({
page: schema.number({ defaultValue: 1, min: 1 }),
start: optionalDateFieldSchema,
end: optionalDateFieldSchema,
sort_field: schema.oneOf(
[
schema.literal('event.start'),
schema.literal('event.end'),
schema.literal('event.provider'),
schema.literal('event.duration'),
schema.literal('event.action'),
schema.literal('message'),
],
{
defaultValue: 'event.start',
}
),
sort_order: schema.oneOf([schema.literal('asc'), schema.literal('desc')], {
defaultValue: 'asc',
}),
});
export type FindOptionsType = TypeOf<typeof findOptionsSchema>;
// page & perPage are required, other fields are optional
// using schema.maybe allows us to set undefined, but not to make the field optional
export type FindOptionsType = Pick<
TypeOf<typeof findOptionsSchema>,
'page' | 'per_page' | 'sort_field' | 'sort_order'
> &
Partial<TypeOf<typeof findOptionsSchema>>;

// note that clusterClient may be null, indicating we can't write to ES
export class EventLogClient implements IEventLogClient {
Expand All @@ -50,13 +73,14 @@ export class EventLogClient implements IEventLogClient {
type: string,
id: string,
options?: Partial<FindOptionsType>
): Promise<IEvent[]> {
): Promise<QueryEventsBySavedObjectResult> {
// veridy the user has the required permissions to view this saved object
await this.savedObjectsClient.get(type, id);
return (await this.esContext.esAdapter.queryEventsBySavedObject(
return await this.esContext.esAdapter.queryEventsBySavedObject(
this.esContext.esNames.alias,
type,
id,
findOptionsSchema.validate(options ?? {})
)) as IEvent[];
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@

import { IEventLogClientService } from './types';

const createEventLogStartMock = () => {
const createEventLogServiceMock = () => {
const mock: jest.Mocked<IEventLogClientService> = {
getClient: jest.fn(),
};
return mock;
};

export const eventLogStartMock = {
create: createEventLogStartMock,
export const eventLogStartServiceMock = {
create: createEventLogServiceMock,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EventLogClientService } from './event_log_client_service';
import { EventLogClientService } from './event_log_start_service';
import { contextMock } from './es/context.mock';
import { KibanaRequest } from 'kibana/server';
import { savedObjectsServiceMock } from 'src/core/server/saved_objects/saved_objects_service.mock';
Expand Down
5 changes: 3 additions & 2 deletions x-pack/plugins/event_log/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
*/

import { eventLogServiceMock } from './event_log_service.mock';
import { eventLogStartServiceMock } from './event_log_start_service.mock';

export { eventLogServiceMock };
export { eventLogServiceMock, eventLogStartServiceMock };
export { eventLoggerMock } from './event_logger.mock';

const createSetupMock = () => {
return eventLogServiceMock.create();
};

const createStartMock = () => {
return undefined;
return eventLogStartServiceMock.create();
};

export const eventLogMock = {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/event_log/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import { findRoute } from './routes';
import { EventLogService } from './event_log_service';
import { createEsContext, EsContext } from './es';
import { EventLogClientService } from './event_log_client_service';
import { EventLogClientService } from './event_log_start_service';

export type PluginClusterClient = Pick<ClusterClient, 'callAsInternalUser' | 'asScoped'>;

Expand Down
Loading

0 comments on commit 2bbac1a

Please sign in to comment.