diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fddc52fa5f527..c4ef8cba67037 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -823,12 +823,13 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations /x-pack/test/stack_functional_integration/apps/management/_index_pattern_create.js @elastic/kibana-data-discovery /x-pack/test/upgrade/apps/discover @elastic/kibana-data-discovery -# Vis Editors +# Visualizations /src/plugins/visualize/ @elastic/kibana-visualizations /x-pack/test/functional/apps/lens @elastic/kibana-visualizations /x-pack/test/api_integration/apis/lens/ @elastic/kibana-visualizations /test/functional/apps/visualize/ @elastic/kibana-visualizations /x-pack/test/functional/apps/graph @elastic/kibana-visualizations +/test/api_integraion/apis/event_annotations @elastic/kibana-visualizations # Global Experience diff --git a/src/plugins/event_annotation/common/content_management/v1/cm_services.ts b/src/plugins/event_annotation/common/content_management/v1/cm_services.ts index 44991124e472b..8da6efc7c7f08 100644 --- a/src/plugins/event_annotation/common/content_management/v1/cm_services.ts +++ b/src/plugins/event_annotation/common/content_management/v1/cm_services.ts @@ -33,7 +33,7 @@ const eventAnnotationGroupAttributesSchema = schema.object( description: schema.maybe(schema.string()), ignoreGlobalFilters: schema.boolean(), annotations: schema.arrayOf(schema.any()), - dataViewSpec: schema.maybe(schema.any()), + dataViewSpec: schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]), }, { unknowns: 'forbid' } ); diff --git a/src/plugins/event_annotation/common/content_management/v1/types.ts b/src/plugins/event_annotation/common/content_management/v1/types.ts index e12a0bc813174..67ba080a78151 100644 --- a/src/plugins/event_annotation/common/content_management/v1/types.ts +++ b/src/plugins/event_annotation/common/content_management/v1/types.ts @@ -34,7 +34,8 @@ export interface EventAnnotationGroupSavedObjectAttributes { description: string; ignoreGlobalFilters: boolean; annotations: EventAnnotationConfig[]; - dataViewSpec?: DataViewSpec; + // NULL is important here - undefined will not properly remove this property from the saved object + dataViewSpec: DataViewSpec | null; } export interface EventAnnotationGroupSavedObject { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts index c122102ae632a..7a0f00e7a4de2 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.test.ts +++ b/src/plugins/event_annotation/public/event_annotation_service/service.test.ts @@ -37,6 +37,7 @@ const annotationGroupResolveMocks: Record = tags: [], ignoreGlobalFilters: false, annotations: [], + dataViewSpec: null, }, type: 'event-annotation-group', references: [ @@ -574,7 +575,7 @@ describe('Event Annotation Service', () => { title: 'newGroupTitle', description: 'my description', ignoreGlobalFilters: false, - dataViewSpec: undefined, + dataViewSpec: null, annotations, }, options: { @@ -624,7 +625,7 @@ describe('Event Annotation Service', () => { title: 'newTitle', description: '', annotations: [], - dataViewSpec: undefined, + dataViewSpec: null, ignoreGlobalFilters: false, } as EventAnnotationGroupSavedObjectAttributes, options: { diff --git a/src/plugins/event_annotation/public/event_annotation_service/service.tsx b/src/plugins/event_annotation/public/event_annotation_service/service.tsx index b45748ed06eaf..4003ea524b291 100644 --- a/src/plugins/event_annotation/public/event_annotation_service/service.tsx +++ b/src/plugins/event_annotation/public/event_annotation_service/service.tsx @@ -218,7 +218,7 @@ export function getEventAnnotationService( description, ignoreGlobalFilters, annotations, - dataViewSpec: dataViewSpec || undefined, + dataViewSpec, }, references, }; diff --git a/test/api_integration/apis/event_annotations/event_annotations.ts b/test/api_integration/apis/event_annotations/event_annotations.ts new file mode 100644 index 0000000000000..01119446d8aa1 --- /dev/null +++ b/test/api_integration/apis/event_annotations/event_annotations.ts @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const CONTENT_ENDPOINT = '/api/content_management/rpc'; + +const CONTENT_TYPE_ID = 'event-annotation-group'; + +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 + +const DEFAULT_EVENT_ANNOTATION_GROUP = { + title: 'a group', + description: '', + ignoreGlobalFilters: true, + dataViewSpec: null, + annotations: [ + { + label: 'Event', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: '2023-08-10T15:00:00.000Z', + }, + icon: 'triangle', + id: '499ee351-f541-46e0-b327-b3dcae91aff5', + }, + ], +}; + +const DEFAULT_REFERENCES = [ + { + type: 'index-pattern', + id: '90943e30-9a47-11e8-b64d-95841ca0b247', + name: 'event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247', + }, +]; + +export default function ({ getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const supertest = getService('supertest'); + + describe('group API', () => { + before(async () => { + await kibanaServer.importExport.load( + 'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json' + ); + }); + + after(async () => { + await kibanaServer.importExport.unload( + 'test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json' + ); + }); + + describe('search', () => { + // TODO test tag searching, ordering, pagination, etc + + it(`should retrieve existing groups`, async () => { + const resp = await supertest + .post(`${CONTENT_ENDPOINT}/search`) + .set('kbn-xsrf', 'kibana') + .send({ + contentTypeId: CONTENT_TYPE_ID, + query: { + limit: 1000, + tags: { + included: [], + excluded: [], + }, + }, + version: API_VERSION, + }) + .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]); + }); + }); + + describe('create', () => { + it(`should require dataViewSpec to be specified`, async () => { + const createWithDataViewSpec = (dataViewSpec: any) => + supertest + .post(`${CONTENT_ENDPOINT}/create`) + .set('kbn-xsrf', 'kibana') + .send({ + contentTypeId: CONTENT_TYPE_ID, + data: { ...DEFAULT_EVENT_ANNOTATION_GROUP, dataViewSpec }, + options: { + references: DEFAULT_REFERENCES, + }, + version: API_VERSION, + }); + + const errorResp = await createWithDataViewSpec(undefined).expect(400); + + expect(errorResp.body.message).to.be( + 'Invalid data. [dataViewSpec]: expected at least one defined value but got [undefined]' + ); + + await createWithDataViewSpec(null).expect(200); + + await createWithDataViewSpec({ + someDataViewProp: 'some-value', + }).expect(200); + }); + }); + + describe('update', () => { + it(`should require dataViewSpec to be specified`, async () => { + const updateWithDataViewSpec = (dataViewSpec: any) => + supertest + .post(`${CONTENT_ENDPOINT}/update`) + .set('kbn-xsrf', 'kibana') + .send({ + contentTypeId: CONTENT_TYPE_ID, + data: { ...DEFAULT_EVENT_ANNOTATION_GROUP, dataViewSpec }, + id: EXISTING_ID_1, + options: { + references: DEFAULT_REFERENCES, + }, + version: API_VERSION, + }); + + const errorResp = await updateWithDataViewSpec(undefined).expect(400); + + expect(errorResp.body.message).to.be( + 'Invalid data. [dataViewSpec]: expected at least one defined value but got [undefined]' + ); + + await updateWithDataViewSpec(null).expect(200); + + await updateWithDataViewSpec({ + someDataViewProp: 'some-value', + }).expect(200); + }); + }); + + // TODO - delete + }); +} diff --git a/test/api_integration/apis/event_annotations/index.ts b/test/api_integration/apis/event_annotations/index.ts new file mode 100644 index 0000000000000..91bcdce64443d --- /dev/null +++ b/test/api_integration/apis/event_annotations/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('event annotations', () => { + loadTestFile(require.resolve('./event_annotations')); + }); +} diff --git a/test/api_integration/apis/index.ts b/test/api_integration/apis/index.ts index 241af3b9a6374..89d97bba330ae 100644 --- a/test/api_integration/apis/index.ts +++ b/test/api_integration/apis/index.ts @@ -17,6 +17,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./home')); loadTestFile(require.resolve('./data_view_field_editor')); loadTestFile(require.resolve('./data_views')); + loadTestFile(require.resolve('./event_annotations')); loadTestFile(require.resolve('./kql_telemetry')); loadTestFile(require.resolve('./saved_objects_management')); loadTestFile(require.resolve('./saved_objects')); diff --git a/test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json b/test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json new file mode 100644 index 0000000000000..ed574c749518c --- /dev/null +++ b/test/api_integration/fixtures/kbn_archiver/event_annotations/event_annotations.json @@ -0,0 +1,95 @@ +{ + "attributes": { + "fieldFormatMap": "{\"hour_of_day\":{}}", + "name": "Kibana Sample Data Logs", + "runtimeFieldMap": "{\"hour_of_day\":{\"type\":\"long\",\"script\":{\"source\":\"emit(doc['timestamp'].value.getHour());\"}}}", + "timeFieldName": "timestamp", + "title": "kibana_sample_data_logs" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-08-15T19:49:25.494Z", + "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" +} + +{ + "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", + "key": { + "type": "point_in_time" + }, + "label": "Event", + "lineStyle": "dashed", + "timeField": "timestamp", + "type": "query" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "Another group" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-08-15T21:02:32.023Z", + "id": "0d1aa670-3baf-11ee-a4a7-c11cb33a9549", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-08-15T22:15:43.724Z", + "version": "WzU4LDFd" +} + +{ + "attributes": { + "annotations": [ + { + "icon": "triangle", + "id": "1d9627a8-11dc-44f1-badb-4d40a80b6bee", + "key": { + "timestamp": "2023-08-10T15:00:00.000Z", + "type": "point_in_time" + }, + "label": "Event", + "type": "manual" + } + ], + "dataViewSpec": null, + "description": "", + "ignoreGlobalFilters": true, + "title": "A group" + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2023-08-15T19:50:29.907Z", + "id": "fcebef20-3ba4-11ee-85d3-3dd00bdd66ef", + "managed": false, + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "event-annotation-group_dataView-ref-90943e30-9a47-11e8-b64d-95841ca0b247", + "type": "index-pattern" + } + ], + "type": "event-annotation-group", + "updated_at": "2023-08-15T22:13:19.290Z", + "version": "WzU0LDFd" +} \ No newline at end of file