From 0313b50a2d722b4a05ac11de1276d4e3dccbd17a Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Tue, 6 Aug 2019 21:12:08 +0200 Subject: [PATCH 1/2] Make type option required for SavedObjects.find --- ...a-plugin-public.savedobjectsclient.find.md | 2 +- ...kibana-plugin-public.savedobjectsclient.md | 2 +- ...gin-public.savedobjectsfindoptions.type.md | 2 +- ...gin-server.savedobjectsfindoptions.type.md | 2 +- src/core/public/public.api.md | 4 +-- .../saved_objects/saved_objects_client.ts | 2 +- .../export/get_sorted_objects_for_export.ts | 27 ++++++++++--------- src/core/server/saved_objects/types.ts | 2 +- src/core/server/server.api.md | 2 +- .../encrypted_saved_objects_client_wrapper.ts | 2 +- .../siem/server/lib/note/saved_object.ts | 9 ++++--- .../server/lib/pinned_event/saved_object.ts | 8 +++--- .../siem/server/lib/timeline/saved_object.ts | 6 ++--- .../spaces_saved_objects_client.test.ts | 4 ++- .../spaces_saved_objects_client.ts | 2 +- 15 files changed, 40 insertions(+), 36 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md index 20b9ff35779f9..80ddb1aea18d1 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md @@ -9,5 +9,5 @@ Search for objects Signature: ```typescript -find: (options?: Pick) => Promise>; +find: (options: Pick) => Promise>; ``` diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md index d0de26bb9b0a1..2ad9591426ab2 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md @@ -20,7 +20,7 @@ export declare class SavedObjectsClient | [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | (objects?: {
id: string;
type: string;
}[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>> | Returns an array of objects by id | | [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>> | Persists an object | | [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | (type: string, id: string) => Promise<{}> | Deletes an object | -| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <T extends SavedObjectAttributes>(options?: Pick<SavedObjectFindOptionsServer, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>> | Search for objects | +| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>> | Search for objects | | [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>> | Fetches a single object | ## Methods diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsfindoptions.type.md b/docs/development/core/public/kibana-plugin-public.savedobjectsfindoptions.type.md index 6706e8344a1e3..97db9bc11c1c4 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsfindoptions.type.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsfindoptions.type.md @@ -7,5 +7,5 @@ Signature: ```typescript -type?: string | string[]; +type: string | string[]; ``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsfindoptions.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsfindoptions.type.md index f22eca5e1474c..95bac5bbc5cb8 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsfindoptions.type.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsfindoptions.type.md @@ -7,5 +7,5 @@ Signature: ```typescript -type?: string | string[]; +type: string | string[]; ``` diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 077dfac06de3a..4e45d326aeb80 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -676,7 +676,7 @@ export class SavedObjectsClient { }[]) => Promise>; create: (type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise>; delete: (type: string, id: string) => Promise<{}>; - find: (options?: Pick) => Promise>; + find: (options: Pick) => Promise>; get: (type: string, id: string) => Promise>; update(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise>; } @@ -714,7 +714,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { // (undocumented) sortOrder?: string; // (undocumented) - type?: string | string[]; + type: string | string[]; } // @public diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index b0768826159cc..dc13d001643a3 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -284,7 +284,7 @@ export class SavedObjectsClient { * @returns A find result with objects matching the specified search. */ public find = ( - options: SavedObjectsFindOptions = {} + options: SavedObjectsFindOptions ): Promise> => { const path = this.getPath(['_find']); const renameMap = { diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts index 08795e9fc7738..b4e7c2887fd3a 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts @@ -52,7 +52,7 @@ async function fetchObjectsToExport({ savedObjectsClient: SavedObjectsClientContract; namespace?: string; }) { - if (objects) { + if (objects && objects.length > 0) { if (objects.length > exportSizeLimit) { throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`); } @@ -66,18 +66,21 @@ async function fetchObjectsToExport({ throw err; } return bulkGetResult.saved_objects; + } else if (types && types.length > 0) { + const findResponse = await savedObjectsClient.find({ + type: types, + sortField: '_id', + sortOrder: 'asc', + perPage: exportSizeLimit, + namespace, + }); + if (findResponse.total > exportSizeLimit) { + throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`); + } + return findResponse.saved_objects; + } else { + throw Boom.badRequest('Either `type` or `objects` are required.'); } - const findResponse = await savedObjectsClient.find({ - type: types, - sortField: '_id', - sortOrder: 'asc', - perPage: exportSizeLimit, - namespace, - }); - if (findResponse.total > exportSizeLimit) { - throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`); - } - return findResponse.saved_objects; } export async function getSortedObjectsForExport({ diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 0966874aa435b..a7e8f5fd4ac7c 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -102,7 +102,7 @@ export interface SavedObjectReference { * @public */ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { - type?: string | string[]; + type: string | string[]; page?: number; perPage?: number; sortField?: string; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index dd889ea1995e0..8d3c179218060 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -759,7 +759,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { // (undocumented) sortOrder?: string; // (undocumented) - type?: string | string[]; + type: string | string[]; } // @public diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts b/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts index 1cef26d08d734..a18e691ae1a1f 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts +++ b/x-pack/legacy/plugins/encrypted_saved_objects/server/lib/encrypted_saved_objects_client_wrapper.ts @@ -114,7 +114,7 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon return await this.options.baseClient.delete(type, id, options); } - public async find(options: SavedObjectsFindOptions = {}) { + public async find(options: SavedObjectsFindOptions) { return this.stripEncryptedAttributesFromBulkResponse( await this.options.baseClient.find(options) ); diff --git a/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts index 7c3b5a3f02612..7352c39d35c6c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/note/saved_object.ts @@ -47,6 +47,7 @@ export class Note { public async deleteNoteByTimelineId(request: FrameworkRequest, timelineId: string) { const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, search: timelineId, searchFields: ['timelineId'], }; @@ -69,6 +70,7 @@ export class Note { eventId: string ): Promise { const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, search: eventId, searchFields: ['eventId'], }; @@ -81,6 +83,7 @@ export class Note { timelineId: string ): Promise { const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, search: timelineId, searchFields: ['timelineId'], }; @@ -95,6 +98,7 @@ export class Note { sort: SortNote | null ): Promise { const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, perPage: pageInfo != null ? pageInfo.pageSize : undefined, page: pageInfo != null ? pageInfo.pageIndex : undefined, search: search != null ? search : undefined, @@ -196,10 +200,7 @@ export class Note { request[internalFrameworkRequest] ); - const savedObjects = await savedObjectsClient.find({ - type: noteSavedObjectType, - ...options, - }); + const savedObjects = await savedObjectsClient.find(options); return { totalCount: savedObjects.total, diff --git a/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts index d07a51d7f94c0..605acfdfbaae5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/pinned_event/saved_object.ts @@ -43,6 +43,7 @@ export class PinnedEvent { public async deleteAllPinnedEventsOnTimeline(request: FrameworkRequest, timelineId: string) { const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, search: timelineId, searchFields: ['timelineId'], }; @@ -68,6 +69,7 @@ export class PinnedEvent { timelineId: string ): Promise { const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, search: timelineId, searchFields: ['timelineId'], }; @@ -81,6 +83,7 @@ export class PinnedEvent { sort: SortNote | null ): Promise { const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, perPage: pageInfo != null ? pageInfo.pageSize : undefined, page: pageInfo != null ? pageInfo.pageIndex : undefined, search: search != null ? search : undefined, @@ -181,10 +184,7 @@ export class PinnedEvent { request[internalFrameworkRequest] ); - const savedObjects = await savedObjectsClient.find({ - type: pinnedEventSavedObjectType, - ...options, - }); + const savedObjects = await savedObjectsClient.find(options); return savedObjects.saved_objects.map(savedObject => convertSavedObjectToSavedPinnedEvent(savedObject) diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts index 4d3203e2c5570..a8fc407e12f14 100644 --- a/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/timeline/saved_object.ts @@ -61,6 +61,7 @@ export class Timeline { sort: SortTimeline | null ): Promise { const options: SavedObjectsFindOptions = { + type: timelineSavedObjectType, perPage: pageInfo != null ? pageInfo.pageSize : undefined, page: pageInfo != null ? pageInfo.pageIndex : undefined, search: search != null ? search : undefined, @@ -266,10 +267,7 @@ export class Timeline { }`; } - const savedObjects = await savedObjectsClient.find({ - type: timelineSavedObjectType, - ...options, - }); + const savedObjects = await savedObjectsClient.find(options); const timelinesWithNotesAndPinnedEvents = await Promise.all( savedObjects.saved_objects.map(async savedObject => { diff --git a/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.test.ts b/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.test.ts index 40cd3200e8f2a..4a5796b2e4ea2 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.test.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.test.ts @@ -139,7 +139,9 @@ const createSpacesService = async (spaceId: string) => { types, }); - await expect(client.find({ namespace: 'bar' })).rejects.toThrowErrorMatchingSnapshot(); + await expect( + client.find({ type: 'foo', namespace: 'bar' }) + ).rejects.toThrowErrorMatchingSnapshot(); }); test(`passes options.type to baseClient if valid singular type specified`, async () => { diff --git a/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts b/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts index 5027413f14154..d47a22e8d4545 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/saved_objects_client/spaces_saved_objects_client.ts @@ -132,7 +132,7 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { * @property {object} [options.hasReference] - { type, id } * @returns {promise} - { saved_objects: [{ id, type, version, attributes }], total, per_page, page } */ - public async find(options: SavedObjectsFindOptions = {}) { + public async find(options: SavedObjectsFindOptions) { throwErrorIfNamespaceSpecified(options); return await this.client.find({ From 6ec36e3b78675c678bce4d4a3bb05628b0608fde Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Fri, 9 Aug 2019 15:18:37 +0200 Subject: [PATCH 2/2] getSortedObjectsForExport test for type or objects --- .../get_sorted_objects_for_export.test.ts | 371 +++++++++--------- 1 file changed, 192 insertions(+), 179 deletions(-) diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts index 618a83b0fb68b..ad2a0e469ddd8 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts @@ -74,51 +74,51 @@ describe('getSortedObjectsForExport()', () => { const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, - ] - `); + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); expect(savedObjectsClient.find).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Object { - "namespace": undefined, - "perPage": 500, - "sortField": "_id", - "sortOrder": "asc", - "type": Array [ - "index-pattern", - "search", + [MockFunction] { + "calls": Array [ + Array [ + Object { + "namespace": undefined, + "perPage": 500, + "sortField": "_id", + "sortOrder": "asc", + "type": Array [ + "index-pattern", + "search", + ], + }, + ], ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('exports from the provided namespace when present', async () => { @@ -157,51 +157,51 @@ describe('getSortedObjectsForExport()', () => { const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, - ] - `); + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); expect(savedObjectsClient.find).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Object { - "namespace": "foo", - "perPage": 500, - "sortField": "_id", - "sortOrder": "asc", - "type": Array [ - "index-pattern", - "search", + [MockFunction] { + "calls": Array [ + Array [ + Object { + "namespace": "foo", + "perPage": 500, + "sortField": "_id", + "sortOrder": "asc", + "type": Array [ + "index-pattern", + "search", + ], + }, + ], ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('export selected types throws error when exceeding exportSizeLimit', async () => { @@ -279,54 +279,54 @@ describe('getSortedObjectsForExport()', () => { }); const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, - ] - `); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ Array [ Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, Object { + "attributes": Object {}, "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], "type": "search", }, - ], - Object { - "namespace": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + Object { + "id": "2", + "type": "search", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('includes nested dependencies when passed in', async () => { @@ -370,65 +370,65 @@ describe('getSortedObjectsForExport()', () => { }); const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, - ] - `); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "id": "2", - "type": "search", - }, - ], - Object { - "namespace": undefined, - }, - ], - Array [ Array [ Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, - ], - Object { - "namespace": undefined, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - Object { - "type": "return", - "value": Promise {}, - }, - ], - } - `); + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "2", + "type": "search", + }, + ], + Object { + "namespace": undefined, + }, + ], + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('export selected objects throws error when exceeding exportSizeLimit', async () => { @@ -451,4 +451,17 @@ describe('getSortedObjectsForExport()', () => { `"Can't export more than 1 objects"` ); }); + + test('rejects when neither type nor objects paramaters are passed in', () => { + const exportOpts = { + exportSizeLimit: 1, + savedObjectsClient, + types: undefined, + objects: undefined, + }; + + expect(getSortedObjectsForExport(exportOpts)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Either \`type\` or \`objects\` are required."` + ); + }); });