[Event annotations] add more API integration tests (#166463)

## Summary

Covers the event annotations API in tests.

Part of https://github.com/elastic/kibana/issues/159053
Part of https://github.com/elastic/kibana/issues/161038

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Drew Tate 2023-09-18 11:45:06 -06:00 committed by GitHub
parent 2a4cb333ec
commit 6d47334528
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 436 additions and 35 deletions

View file

@ -79,7 +79,6 @@ const getResultSchema = schema.object(
);
const createOptionsSchema = schema.object({
overwrite: schema.maybe(schema.boolean()),
references: schema.maybe(referencesSchema),
});

View file

@ -79,8 +79,6 @@ export type EventAnnotationGroupGetOut = GetResult<
// ----------- CREATE --------------
export interface CreateOptions {
/** If a document with the given `id` already exists, overwrite it's contents (default=false). */
overwrite?: boolean;
/** Array of referenced saved objects. */
references?: Reference[];
}

View file

@ -27,10 +27,16 @@ export type { FetchEventAnnotationsArgs } from './fetch_event_annotations/types'
export type { EventAnnotationArgs, EventAnnotationOutput } from './types';
export type {
EventAnnotationGroupGetIn,
EventAnnotationGroupGetOut,
EventAnnotationGroupSavedObjectAttributes,
EventAnnotationGroupCreateIn,
EventAnnotationGroupCreateOut,
EventAnnotationGroupUpdateIn,
EventAnnotationGroupSearchIn,
EventAnnotationGroupSearchOut,
EventAnnotationGroupDeleteIn,
EventAnnotationGroupDeleteOut,
} from './content_management';
export { CONTENT_ID } from './content_management';
export { ANNOTATIONS_LISTING_VIEW_ID } from './constants';

View file

@ -121,7 +121,6 @@ export class EventAnnotationGroupStorage
const transforms = getTransforms(cmServicesDefinition, requestVersion);
const soClient = await savedObjectClientFromRequest(ctx);
// Save data in DB
const {
saved_object: savedObject,
alias_purpose: aliasPurpose,

View file

@ -10,18 +10,29 @@ import expect from '@kbn/expect';
import type {
EventAnnotationGroupSavedObjectAttributes,
EventAnnotationGroupCreateIn,
EventAnnotationGroupCreateOut,
EventAnnotationGroupUpdateIn,
EventAnnotationGroupSearchIn,
EventAnnotationGroupSearchOut,
EventAnnotationGroupGetIn,
EventAnnotationGroupGetOut,
EventAnnotationGroupDeleteIn,
EventAnnotationGroupDeleteOut,
} from '@kbn/event-annotation-plugin/common';
import { CONTENT_ID } from '@kbn/event-annotation-plugin/common';
import { EVENT_ANNOTATION_GROUP_TYPE } from '@kbn/event-annotation-common';
import { FtrProviderContext } from '../../ftr_provider_context';
const CONTENT_ENDPOINT = '/api/content_management/rpc';
const API_VERSION = 1;
const EXISTING_ID_1 = 'fcebef20-3ba4-11ee-85d3-3dd00bdd66ef'; // from loaded archive
const EXISTING_ID_2 = '0d1aa670-3baf-11ee-a4a7-c11cb33a9549'; // from loaded archive
// IDs come from from loaded archive
const EXISTING_ID_1 = '46c2a460-4e77-11ee-bb97-116581699678';
const EXISTING_ID_2 = '425d2760-4e77-11ee-bb97-116581699678';
const DESCRIPTION_2 = 'i am a description you can search for!';
const EXISTING_ID_3 = '3905a4d0-4e77-11ee-bb97-116581699678';
const TAG_ID = '36a8f020-4e77-11ee-bb97-116581699678';
const DEFAULT_EVENT_ANNOTATION_GROUP: EventAnnotationGroupSavedObjectAttributes = {
title: 'a group',
@ -56,6 +67,8 @@ export default function ({ getService }: FtrProviderContext) {
describe('group API', () => {
before(async () => {
await kibanaServer.savedObjects.clean({ types: [EVENT_ANNOTATION_GROUP_TYPE] });
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json'
);
@ -65,10 +78,79 @@ export default function ({ getService }: FtrProviderContext) {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json'
);
await kibanaServer.savedObjects.clean({ types: [EVENT_ANNOTATION_GROUP_TYPE] });
});
describe('get', () => {
it(`should retrieve an existing group`, async () => {
const payload: EventAnnotationGroupGetIn = {
contentTypeId: CONTENT_ID,
id: EXISTING_ID_1,
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/get`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(200);
const result = resp.body.result.result as EventAnnotationGroupGetOut;
expect(result.item.id).to.be(EXISTING_ID_1);
expect(result.meta.outcome).to.be('exactMatch');
expect(result.item.references.length).to.be(1);
expect(result.item.attributes).to.eql({
annotations: [
{
filter: {
language: 'kuery',
query:
'agent.keyword : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)" ',
type: 'kibana_query',
},
icon: 'triangle',
id: 'fdede168-eff1-400f-b106-0f62061f5099',
key: {
type: 'point_in_time',
},
label: 'Event',
timeField: 'timestamp',
type: 'query',
},
],
dataViewSpec: null,
description: '',
ignoreGlobalFilters: true,
title: 'group3',
});
});
it(`should reject a group that does not exist`, async () => {
const payload: EventAnnotationGroupGetIn = {
contentTypeId: CONTENT_ID,
id: 'does-not-exist',
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/get`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(404);
expect(resp.body).to.eql({
error: 'Not Found',
message: 'Saved object [event-annotation-group/does-not-exist] not found',
statusCode: 404,
});
});
});
describe('search', () => {
// TODO test tag searching, ordering, pagination, etc
const performSearch = (payload: EventAnnotationGroupSearchIn) =>
supertest.post(`${CONTENT_ENDPOINT}/search`).set('kbn-xsrf', 'kibana').send(payload);
it(`should retrieve existing groups`, async () => {
const payload: EventAnnotationGroupSearchIn = {
@ -83,19 +165,172 @@ export default function ({ getService }: FtrProviderContext) {
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/search`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(200);
const resp = await performSearch(payload).expect(200);
const results = resp.body.result.result.hits;
expect(results.length).to.be(2);
expect(results.map(({ id }: { id: string }) => id)).to.eql([EXISTING_ID_2, EXISTING_ID_1]);
expect(results.length).to.be(3);
expect(results.map(({ id }: { id: string }) => id)).to.eql([
EXISTING_ID_1,
EXISTING_ID_2,
EXISTING_ID_3,
]);
});
it(`should filter by tag`, async () => {
const payload: EventAnnotationGroupSearchIn = {
contentTypeId: CONTENT_ID,
query: {
limit: 1000,
tags: {
included: [TAG_ID],
excluded: [],
},
},
version: API_VERSION,
};
const resp = await performSearch(payload).expect(200);
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
expect(result.hits.length).to.be(2);
expect(
result.hits.every(({ references }) => references.map(({ id }) => id).includes(TAG_ID))
).to.be(true);
});
it(`should filter by text`, async () => {
const payload: EventAnnotationGroupSearchIn = {
contentTypeId: CONTENT_ID,
query: {
limit: 1000,
text: DESCRIPTION_2,
tags: {
included: [],
excluded: [],
},
},
version: API_VERSION,
};
const resp = await performSearch(payload).expect(200);
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
expect(result.hits.length).to.be(1);
expect(result.hits[0].id).to.be(EXISTING_ID_2);
});
it(`should paginate`, async () => {
const payload: EventAnnotationGroupSearchIn = {
contentTypeId: CONTENT_ID,
query: {
limit: 1,
cursor: '1',
tags: {
included: [],
excluded: [],
},
},
version: API_VERSION,
};
const resp = await performSearch(payload).expect(200);
const result = resp.body.result.result as EventAnnotationGroupSearchOut;
expect(result.hits.length).to.be(1);
expect(result.hits[0].id).to.be(EXISTING_ID_1);
expect(result.pagination.total).to.be(3);
// get second page
payload.query.cursor = '2';
const resp2 = await performSearch(payload).expect(200);
const result2 = resp2.body.result.result as EventAnnotationGroupSearchOut;
expect(result2.hits.length).to.be(1);
expect(result2.hits[0].id).to.be(EXISTING_ID_2);
expect(result2.pagination.total).to.be(3);
// get third page
payload.query.cursor = '3';
const resp3 = await performSearch(payload).expect(200);
const result3 = resp3.body.result.result as EventAnnotationGroupSearchOut;
expect(result3.hits.length).to.be(1);
expect(result3.hits[0].id).to.be(EXISTING_ID_3);
expect(result3.pagination.total).to.be(3);
});
});
describe('create', () => {
it(`should create a new group`, async () => {
const payload: EventAnnotationGroupCreateIn = {
contentTypeId: CONTENT_ID,
data: DEFAULT_EVENT_ANNOTATION_GROUP,
options: {
references: DEFAULT_REFERENCES,
},
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/create`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(200);
const result = resp.body.result.result as EventAnnotationGroupCreateOut;
expect(result.item.attributes).to.eql(DEFAULT_EVENT_ANNOTATION_GROUP);
expect(result.item.id).to.be.a('string');
expect(result.item.namespaces).to.eql(['default']);
});
it(`should reject malformed groups`, async () => {
const badGroups = [
// extra property
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
extraProp: 'some-value',
},
// missing title
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
title: undefined,
},
// wrong type for property
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
ignoreGlobalFilters: 'not-a-boolean',
},
] as unknown as EventAnnotationGroupSavedObjectAttributes[]; // (coerce the types because these are intentionally malformed)
const expectedMessages = [
'Invalid data. [extraProp]: definition for this key is missing',
'Invalid data. [title]: expected value of type [string] but got [undefined]',
'Invalid data. [ignoreGlobalFilters]: expected value of type [boolean] but got [string]',
];
for (let i = 0; i < badGroups.length; i++) {
const payload: EventAnnotationGroupCreateIn = {
contentTypeId: CONTENT_ID,
data: badGroups[i],
options: {
references: DEFAULT_REFERENCES,
},
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/create`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(400);
expect(resp.body.message).to.be(expectedMessages[i]);
}
});
it(`should require dataViewSpec to be specified`, async () => {
const createWithDataViewSpec = (dataViewSpec: any) => {
const payload: EventAnnotationGroupCreateIn = {
@ -127,6 +362,81 @@ export default function ({ getService }: FtrProviderContext) {
});
describe('update', () => {
it(`should update a group`, async () => {
const payload: EventAnnotationGroupUpdateIn = {
contentTypeId: CONTENT_ID,
data: {
...DEFAULT_EVENT_ANNOTATION_GROUP,
description: 'updated description',
},
id: EXISTING_ID_1,
options: {
references: DEFAULT_REFERENCES,
},
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/update`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(200);
const result = resp.body.result.result as EventAnnotationGroupCreateOut;
expect(result.item.attributes).to.eql({
...DEFAULT_EVENT_ANNOTATION_GROUP,
description: 'updated description',
});
expect(result.item.id).to.be(EXISTING_ID_1);
});
it(`should reject malformed groups`, async () => {
const badGroups = [
// extra property
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
extraProp: 'some-value',
},
// missing title
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
title: undefined,
},
// wrong type for property
{
...DEFAULT_EVENT_ANNOTATION_GROUP,
ignoreGlobalFilters: 'not-a-boolean',
},
] as unknown as EventAnnotationGroupSavedObjectAttributes[]; // (coerce the types because these are intentionally malformed)
const expectedMessages = [
'Invalid data. [extraProp]: definition for this key is missing',
'Invalid data. [title]: expected value of type [string] but got [undefined]',
'Invalid data. [ignoreGlobalFilters]: expected value of type [boolean] but got [string]',
];
for (let i = 0; i < badGroups.length; i++) {
const payload: EventAnnotationGroupUpdateIn = {
contentTypeId: CONTENT_ID,
data: badGroups[i],
id: EXISTING_ID_1,
options: {
references: DEFAULT_REFERENCES,
},
version: API_VERSION,
};
const resp = await supertest
.post(`${CONTENT_ENDPOINT}/update`)
.set('kbn-xsrf', 'kibana')
.send(payload)
.expect(400);
expect(resp.body.message).to.be(expectedMessages[i]);
}
});
it(`should require dataViewSpec to be specified`, async () => {
const updateWithDataViewSpec = (dataViewSpec: any) => {
const payload: EventAnnotationGroupUpdateIn = {
@ -158,6 +468,34 @@ export default function ({ getService }: FtrProviderContext) {
});
});
// TODO - delete
describe('delete', () => {
const deleteGroupByID = (id: string) => {
const payload: EventAnnotationGroupDeleteIn = {
contentTypeId: CONTENT_ID,
id,
version: API_VERSION,
};
return supertest.post(`${CONTENT_ENDPOINT}/delete`).set('kbn-xsrf', 'kibana').send(payload);
};
it(`should delete a group`, async () => {
const resp = await deleteGroupByID(EXISTING_ID_1).expect(200);
const result = resp.body.result.result as EventAnnotationGroupDeleteOut;
expect(result.success).to.be(true);
});
it(`should reject deleting a group that does not exist`, async () => {
const resp = await deleteGroupByID('does-not-exist').expect(404);
expect(resp.body).to.eql({
error: 'Not Found',
message: 'Saved object [event-annotation-group/does-not-exist] not found',
statusCode: 404,
});
});
});
});
}

View file

@ -7,33 +7,31 @@
"title": "kibana_sample_data_logs"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2023-08-15T19:49:25.494Z",
"created_at": "2023-09-07T17:23:20.906Z",
"id": "90943e30-9a47-11e8-b64d-95841ca0b247",
"managed": false,
"references": [],
"type": "index-pattern",
"typeMigrationVersion": "8.0.0",
"updated_at": "2023-08-15T19:49:25.494Z",
"version": "WzIyLDFd"
"updated_at": "2023-09-07T17:23:20.906Z",
"version": "WzEwMiwxXQ=="
}
{
"attributes": {
"annotations": [
{
"color": "#6092c0",
"filter": {
"language": "kuery",
"query": "agent.keyword : \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\" ",
"type": "kibana_query"
},
"icon": "asterisk",
"id": "499ee351-f541-46e0-b327-b3dcae91aff5",
"icon": "triangle",
"id": "fdede168-eff1-400f-b106-0f62061f5099",
"key": {
"type": "point_in_time"
},
"label": "Event",
"lineStyle": "dashed",
"timeField": "timestamp",
"type": "query"
}
@ -41,11 +39,11 @@
"dataViewSpec": null,
"description": "",
"ignoreGlobalFilters": true,
"title": "Another group"
"title": "group3"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2023-08-15T21:02:32.023Z",
"id": "0d1aa670-3baf-11ee-a4a7-c11cb33a9549",
"created_at": "2023-09-08T18:41:09.030Z",
"id": "46c2a460-4e77-11ee-bb97-116581699678",
"managed": false,
"references": [
{
@ -55,8 +53,25 @@
}
],
"type": "event-annotation-group",
"updated_at": "2023-08-15T22:15:43.724Z",
"version": "WzU4LDFd"
"updated_at": "2023-09-08T18:42:48.080Z",
"version": "WzE2OCwxXQ=="
}
{
"attributes": {
"color": "#dac7c4",
"description": "a tag to filter by",
"name": "tag"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2023-09-08T18:40:42.018Z",
"id": "36a8f020-4e77-11ee-bb97-116581699678",
"managed": false,
"references": [],
"type": "tag",
"typeMigrationVersion": "8.0.0",
"updated_at": "2023-09-08T18:40:42.018Z",
"version": "WzI3NDUsMV0="
}
{
@ -64,9 +79,49 @@
"annotations": [
{
"icon": "triangle",
"id": "1d9627a8-11dc-44f1-badb-4d40a80b6bee",
"id": "3d9f03ca-36aa-4ebb-aab8-efef1591c6d5",
"key": {
"timestamp": "2023-08-10T15:00:00.000Z",
"timestamp": "2023-09-08T18:32:00.000Z",
"type": "point_in_time"
},
"label": "Event",
"type": "manual"
}
],
"dataViewSpec": null,
"description": "i am a description you can search for!",
"ignoreGlobalFilters": true,
"title": "group2"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2023-09-08T18:41:01.654Z",
"id": "425d2760-4e77-11ee-bb97-116581699678",
"managed": false,
"references": [
{
"id": "90943e30-9a47-11e8-b64d-95841ca0b247",
"name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247",
"type": "index-pattern"
},
{
"id": "36a8f020-4e77-11ee-bb97-116581699678",
"name": "36a8f020-4e77-11ee-bb97-116581699678",
"type": "tag"
}
],
"type": "event-annotation-group",
"updated_at": "2023-09-08T18:41:01.654Z",
"version": "WzE2NCwxXQ=="
}
{
"attributes": {
"annotations": [
{
"icon": "triangle",
"id": "f7be288b-8adf-48b9-89d4-267db4863a3d",
"key": {
"timestamp": "2023-09-08T18:32:00.000Z",
"type": "point_in_time"
},
"label": "Event",
@ -76,20 +131,25 @@
"dataViewSpec": null,
"description": "",
"ignoreGlobalFilters": true,
"title": "A group"
"title": "group1"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2023-08-15T19:50:29.907Z",
"id": "fcebef20-3ba4-11ee-85d3-3dd00bdd66ef",
"created_at": "2023-09-08T18:40:45.981Z",
"id": "3905a4d0-4e77-11ee-bb97-116581699678",
"managed": false,
"references": [
{
"id": "90943e30-9a47-11e8-b64d-95841ca0b247",
"name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247",
"type": "index-pattern"
},
{
"id": "36a8f020-4e77-11ee-bb97-116581699678",
"name": "36a8f020-4e77-11ee-bb97-116581699678",
"type": "tag"
}
],
"type": "event-annotation-group",
"updated_at": "2023-08-15T22:13:19.290Z",
"version": "WzU0LDFd"
"updated_at": "2023-09-08T18:40:45.981Z",
"version": "WzE2MiwxXQ=="
}

View file

@ -68,6 +68,7 @@
"@kbn/core-saved-objects-server",
"@kbn/discover-plugin",
"@kbn/core-http-common",
"@kbn/event-annotation-plugin"
"@kbn/event-annotation-plugin",
"@kbn/event-annotation-common"
]
}