[Core] Make type option required for SavedObjects.find (#42236) (#43112)

* Make type option required for SavedObjects.find

* getSortedObjectsForExport test for type or objects
This commit is contained in:
Rudolf Meijering 2019-08-12 19:55:14 +02:00 committed by GitHub
parent d2a52c0888
commit 46abb3a1db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 232 additions and 215 deletions

View file

@ -9,5 +9,5 @@ Search for objects
<b>Signature:</b>
```typescript
find: <T extends SavedObjectAttributes>(options?: Pick<SavedObjectFindOptionsServer, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
```

View file

@ -20,7 +20,7 @@ export declare class SavedObjectsClient
| [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | <code>(objects?: {</code><br/><code> id: string;</code><br/><code> type: string;</code><br/><code> }[]) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;SavedObjectAttributes&gt;&gt;</code> | Returns an array of objects by id |
| [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, attributes: T, options?: SavedObjectsCreateOptions) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Persists an object |
| [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string) =&gt; Promise&lt;{}&gt;</code> | Deletes an object |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options?: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;type&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;searchFields&quot; &#124; &quot;sortField&quot; &#124; &quot;hasReference&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot; &#124; &quot;fields&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;type&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;searchFields&quot; &#124; &quot;sortField&quot; &#124; &quot;hasReference&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot; &#124; &quot;fields&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, id: string) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Fetches a single object |
## Methods

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
type?: string | string[];
type: string | string[];
```

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
type?: string | string[];
type: string | string[];
```

View file

@ -676,7 +676,7 @@ export class SavedObjectsClient {
}[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
delete: (type: string, id: string) => Promise<{}>;
find: <T extends SavedObjectAttributes>(options?: Pick<SavedObjectsFindOptions, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "type" | "defaultSearchOperator" | "searchFields" | "sortField" | "hasReference" | "page" | "perPage" | "fields">) => Promise<SavedObjectsFindResponsePublic<T>>;
get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
}
@ -714,7 +714,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions {
// (undocumented)
sortOrder?: string;
// (undocumented)
type?: string | string[];
type: string | string[];
}
// @public

View file

@ -284,7 +284,7 @@ export class SavedObjectsClient {
* @returns A find result with objects matching the specified search.
*/
public find = <T extends SavedObjectAttributes>(
options: SavedObjectsFindOptions = {}
options: SavedObjectsFindOptions
): Promise<SavedObjectsFindResponsePublic<T>> => {
const path = this.getPath(['_find']);
const renameMap = {

View file

@ -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."`
);
});
});

View file

@ -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({

View file

@ -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;

View file

@ -779,7 +779,7 @@ export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions {
// (undocumented)
sortOrder?: string;
// (undocumented)
type?: string | string[];
type: string | string[];
}
// @public

View file

@ -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)
);

View file

@ -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<NoteSavedObject[]> {
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
search: eventId,
searchFields: ['eventId'],
};
@ -81,6 +83,7 @@ export class Note {
timelineId: string
): Promise<NoteSavedObject[]> {
const options: SavedObjectsFindOptions = {
type: noteSavedObjectType,
search: timelineId,
searchFields: ['timelineId'],
};
@ -95,6 +98,7 @@ export class Note {
sort: SortNote | null
): Promise<ResponseNotes> {
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,

View file

@ -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<PinnedEventSavedObject[]> {
const options: SavedObjectsFindOptions = {
type: pinnedEventSavedObjectType,
search: timelineId,
searchFields: ['timelineId'],
};
@ -81,6 +83,7 @@ export class PinnedEvent {
sort: SortNote | null
): Promise<PinnedEventSavedObject[]> {
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)

View file

@ -61,6 +61,7 @@ export class Timeline {
sort: SortTimeline | null
): Promise<ResponseTimelines> {
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 => {

View file

@ -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 () => {

View file

@ -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({