diff --git a/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/__tests__/objects_table.test.js b/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/__tests__/objects_table.test.js index 5603f812135c4..e3696cf9393eb 100644 --- a/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/__tests__/objects_table.test.js +++ b/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/__tests__/objects_table.test.js @@ -96,8 +96,8 @@ $http.post = jest.fn().mockImplementation(() => ([])); const defaultProps = { savedObjectsClient: { find: jest.fn().mockImplementation(({ type }) => { - // We pass in type when fetching counts - if (type) { + // We pass in a single type when fetching counts + if (type && !Array.isArray(type)) { return { total: 1, savedObjects: [ diff --git a/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js b/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js index 7a42983d21dd9..5b99a238bc641 100644 --- a/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js +++ b/src/core_plugins/kibana/public/management/sections/objects/components/objects_table/objects_table.js @@ -92,13 +92,13 @@ export class ObjectsTable extends Component { fetchCounts = async () => { const { queryText, visibleTypes } = parseQuery(this.state.activeQuery); - const includeTypes = INCLUDED_TYPES.filter( + const type = INCLUDED_TYPES.filter( type => !visibleTypes || visibleTypes.includes(type) ); const savedObjectCounts = await getSavedObjectCounts( this.props.$http, - includeTypes, + type, queryText ); @@ -130,7 +130,7 @@ export class ObjectsTable extends Component { let savedObjects = []; let totalItemCount = 0; - const includeTypes = INCLUDED_TYPES.filter( + const type = INCLUDED_TYPES.filter( type => !visibleTypes || visibleTypes.includes(type) ); @@ -144,7 +144,7 @@ export class ObjectsTable extends Component { sortField: 'type', fields: ['title', 'id'], searchFields: ['title'], - includeTypes, + type, }); savedObjects = data.savedObjects.map(savedObject => ({ diff --git a/src/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js b/src/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js index 36b1b75f872f6..db330eccace4d 100644 --- a/src/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js +++ b/src/core_plugins/kibana/server/routes/api/management/saved_objects/scroll.js @@ -65,7 +65,7 @@ export function registerScrollForCountRoute(server) { handler: async (req, reply) => { const savedObjectsClient = req.getSavedObjectsClient(); const findOptions = { - includeTypes: req.payload.typesToInclude, + type: req.payload.typesToInclude, perPage: 1000, }; diff --git a/src/server/mappings/index.js b/src/server/mappings/index.js index 2c269e661ce78..bcbe84c080d4b 100644 --- a/src/server/mappings/index.js +++ b/src/server/mappings/index.js @@ -7,4 +7,5 @@ export { getRootType, getProperty, getRootProperties, + getRootPropertiesObjects, } from './lib'; diff --git a/src/server/mappings/lib/get_root_properties_objects.js b/src/server/mappings/lib/get_root_properties_objects.js new file mode 100644 index 0000000000000..e25e6b27a4d95 --- /dev/null +++ b/src/server/mappings/lib/get_root_properties_objects.js @@ -0,0 +1,28 @@ +import { getRootProperties } from './get_root_properties'; + +/** + * Get the property mappings for the root type in the EsMappingsDsl + * where the properties are objects + * + * If the mappings don't have a root type, or the root type is not + * an object type (it's a keyword or something) this function will + * throw an error. + * + * This data can be found at `{indexName}.mappings.{typeName}.properties` + * in the es indices.get() response where the properties are objects. + * + * @param {EsMappingsDsl} mappings + * @return {EsPropertyMappings} + */ +export function getRootPropertiesObjects(mappings) { + const rootProperties = getRootProperties(mappings); + return Object.entries(rootProperties).reduce((acc, [key, value]) => { + + // we consider the existence of the properties or type of object to designate that this is an object datatype + if (value.properties || value.type === 'object') { + acc[key] = value; + } + + return acc; + }, {}); +} diff --git a/src/server/mappings/lib/get_root_properties_objects.test.js b/src/server/mappings/lib/get_root_properties_objects.test.js new file mode 100644 index 0000000000000..adaffcc518c70 --- /dev/null +++ b/src/server/mappings/lib/get_root_properties_objects.test.js @@ -0,0 +1,166 @@ +import { getRootPropertiesObjects } from './get_root_properties_objects'; + +test(`returns single object with properties`, () => { + const mappings = { + rootType: { + properties: { + foo: { + properties: {} + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + properties: {} + } + }); +}); + +test(`returns single object with type === 'object'`, () => { + const mappings = { + rootType: { + properties: { + foo: { + type: 'object' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + type: 'object' + } + }); +}); + +test(`returns two objects with properties`, () => { + const mappings = { + rootType: { + properties: { + foo: { + properties: {} + }, + bar: { + properties: {} + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + properties: {} + }, + bar: { + properties: {} + } + }); +}); + +test(`returns two objects with type === 'object'`, () => { + const mappings = { + rootType: { + properties: { + foo: { + type: 'object' + }, + bar: { + type: 'object' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + type: 'object' + }, + bar: { + type: 'object' + } + }); +}); + +test(`excludes objects without properties and type of keyword`, () => { + const mappings = { + rootType: { + properties: { + foo: { + type: 'keyword' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({}); +}); + +test(`excludes two objects without properties and type of keyword`, () => { + const mappings = { + rootType: { + properties: { + foo: { + type: 'keyword' + }, + bar: { + type: 'keyword' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({}); +}); + +test(`includes one object with properties and excludes one object without properties`, () => { + const mappings = { + rootType: { + properties: { + foo: { + properties: {} + }, + bar: { + type: 'keyword' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + properties: {} + } + }); +}); + +test(`includes one object with type === 'object' and excludes one object without properties`, () => { + const mappings = { + rootType: { + properties: { + foo: { + type: 'object' + }, + bar: { + type: 'keyword' + } + } + } + }; + + const result = getRootPropertiesObjects(mappings); + expect(result).toEqual({ + foo: { + type: 'object' + } + }); +}); diff --git a/src/server/mappings/lib/index.js b/src/server/mappings/lib/index.js index 7998922732298..716d87b571a3f 100644 --- a/src/server/mappings/lib/index.js +++ b/src/server/mappings/lib/index.js @@ -2,3 +2,4 @@ export { getProperty } from './get_property'; export { getTypes } from './get_types'; export { getRootType } from './get_root_type'; export { getRootProperties } from './get_root_properties'; +export { getRootPropertiesObjects } from './get_root_properties_objects'; diff --git a/src/server/saved_objects/client/lib/repository.js b/src/server/saved_objects/client/lib/repository.js index a3e4e19b7e360..f2147b33ce1fc 100644 --- a/src/server/saved_objects/client/lib/repository.js +++ b/src/server/saved_objects/client/lib/repository.js @@ -184,7 +184,7 @@ export class SavedObjectsRepository { /** * @param {object} [options={}] - * @property {string} [options.type] + * @property {(string|Array)} [options.type] * @property {string} [options.search] * @property {Array} [options.searchFields] - see Elasticsearch Simple Query String * Query field argument for more information @@ -205,7 +205,6 @@ export class SavedObjectsRepository { sortField, sortOrder, fields, - includeTypes, } = options; if (searchFields && !Array.isArray(searchFields)) { @@ -228,7 +227,6 @@ export class SavedObjectsRepository { search, searchFields, type, - includeTypes, sortField, sortOrder }) diff --git a/src/server/saved_objects/client/lib/repository.test.js b/src/server/saved_objects/client/lib/repository.test.js index c7833f33ea93b..716327871fc39 100644 --- a/src/server/saved_objects/client/lib/repository.test.js +++ b/src/server/saved_objects/client/lib/repository.test.js @@ -376,7 +376,6 @@ describe('SavedObjectsRepository', () => { type: 'bar', sortField: 'name', sortOrder: 'desc', - includeTypes: ['index-pattern', 'dashboard'], }; await savedObjectsRepository.find(relevantOpts); diff --git a/src/server/saved_objects/client/lib/search_dsl/__snapshots__/sorting_params.test.js.snap b/src/server/saved_objects/client/lib/search_dsl/__snapshots__/sorting_params.test.js.snap new file mode 100644 index 0000000000000..aca0c6ac6a389 --- /dev/null +++ b/src/server/saved_objects/client/lib/search_dsl/__snapshots__/sorting_params.test.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`searchDsl/getSortParams sort with direction sortField is non-root multi-field with multiple types returns correct params 1`] = `"Unable to sort multiple types by field title.raw, not a root field"`; + +exports[`searchDsl/getSortParams sort with direction sortFields is non-root simple property with multiple types returns correct params 1`] = `"Unable to sort multiple types by field title, not a root field"`; + +exports[`searchDsl/getSortParams sortField no direction sortField is not-root multi-field with multiple types returns correct params 1`] = `"Unable to sort multiple types by field title.raw, not a root field"`; + +exports[`searchDsl/getSortParams sortField no direction sortField is simple non-root property with multiple types returns correct params 1`] = `"Unable to sort multiple types by field title, not a root field"`; diff --git a/src/server/saved_objects/client/lib/search_dsl/query_params.js b/src/server/saved_objects/client/lib/search_dsl/query_params.js index c77843233ea54..fee12bde23c1e 100644 --- a/src/server/saved_objects/client/lib/search_dsl/query_params.js +++ b/src/server/saved_objects/client/lib/search_dsl/query_params.js @@ -1,12 +1,31 @@ -import { getRootProperties } from '../../../../mappings'; +import { getRootPropertiesObjects } from '../../../../mappings'; + +/** + * Gets the types based on the type. Uses mappings to support + * null type (all types), a single type string or an array + * @param {EsMapping} mappings + * @param {(string|Array)} type + */ +function getTypes(mappings, type) { + if (!type) { + return Object.keys(getRootPropertiesObjects(mappings)); + } + + if (Array.isArray(type)) { + return type; + } + + return [type]; +} /** * Get the field params based on the types and searchFields * @param {Array} searchFields - * @param {Array} types + * @param {string|Array} types * @return {Object} */ function getFieldsForTypes(searchFields, types) { + if (!searchFields || !searchFields.length) { return { all_fields: true @@ -24,27 +43,13 @@ function getFieldsForTypes(searchFields, types) { /** * Get the "query" related keys for the search body * @param {EsMapping} mapping mappings from Ui - * @param {Object} type + * @param {(string|Array)} type * @param {String} search * @param {Array} searchFields * @return {Object} */ -export function getQueryParams(mappings, type, search, searchFields, includeTypes) { +export function getQueryParams(mappings, type, search, searchFields) { if (!type && !search) { - if (includeTypes) { - return { - query: { - bool: { - should: includeTypes.map(includeType => ({ - term: { - type: includeType, - } - })) - } - } - }; - } - return {}; } @@ -52,21 +57,7 @@ export function getQueryParams(mappings, type, search, searchFields, includeType if (type) { bool.filter = [ - { term: { type } } - ]; - } - - if (includeTypes) { - bool.must = [ - { - bool: { - should: includeTypes.map(includeType => ({ - term: { - type: includeType, - } - })), - } - } + { [Array.isArray(type) ? 'terms' : 'term']: { type } } ]; } @@ -78,7 +69,7 @@ export function getQueryParams(mappings, type, search, searchFields, includeType query: search, ...getFieldsForTypes( searchFields, - type ? [type] : Object.keys(getRootProperties(mappings)), + getTypes(mappings, type) ) } } diff --git a/src/server/saved_objects/client/lib/search_dsl/query_params.test.js b/src/server/saved_objects/client/lib/search_dsl/query_params.test.js index 19316fd46a588..002d6b36bac40 100644 --- a/src/server/saved_objects/client/lib/search_dsl/query_params.test.js +++ b/src/server/saved_objects/client/lib/search_dsl/query_params.test.js @@ -45,7 +45,7 @@ describe('searchDsl/queryParams', () => { }); describe('{type}', () => { - it('includes just a terms filter', () => { + it('adds a term filter when a string', () => { expect(getQueryParams(MAPPINGS, 'saved')) .toEqual({ query: { @@ -59,6 +59,21 @@ describe('searchDsl/queryParams', () => { } }); }); + + it('adds a terms filter when an array', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'])) + .toEqual({ + query: { + bool: { + filter: [ + { + terms: { type: ['saved', 'vis'] } + } + ] + } + } + }); + }); }); describe('{search}', () => { @@ -102,6 +117,26 @@ describe('searchDsl/queryParams', () => { } }); }); + it('includes bool with sqs query and terms filter for type', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'], 'y*')) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { type: ['saved', 'vis'] } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + all_fields: true + } + } + ] + } + } + }); + }); }); describe('{search,searchFields}', () => { @@ -115,7 +150,6 @@ describe('searchDsl/queryParams', () => { simple_query_string: { query: 'y*', fields: [ - 'type.title', 'pending.title', 'saved.title' ] @@ -136,7 +170,6 @@ describe('searchDsl/queryParams', () => { simple_query_string: { query: 'y*', fields: [ - 'type.title^3', 'pending.title^3', 'saved.title^3' ] @@ -157,10 +190,8 @@ describe('searchDsl/queryParams', () => { simple_query_string: { query: 'y*', fields: [ - 'type.title', 'pending.title', 'saved.title', - 'type.title.raw', 'pending.title.raw', 'saved.title.raw', ] @@ -174,7 +205,7 @@ describe('searchDsl/queryParams', () => { }); describe('{type,search,searchFields}', () => { - it('includes bool, and sqs with field list', () => { + it('includes bool, with term filter and sqs with field list', () => { expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title'])) .toEqual({ query: { @@ -196,6 +227,29 @@ describe('searchDsl/queryParams', () => { } }); }); + it('includes bool, with terms filter and sqs with field list', () => { + expect(getQueryParams(MAPPINGS, ['saved', 'vis'], 'y*', ['title'])) + .toEqual({ + query: { + bool: { + filter: [ + { terms: { type: ['saved', 'vis'] } } + ], + must: [ + { + simple_query_string: { + query: 'y*', + fields: [ + 'saved.title', + 'vis.title' + ] + } + } + ] + } + } + }); + }); it('supports fields pointing to multi-fields', () => { expect(getQueryParams(MAPPINGS, 'saved', 'y*', ['title.raw'])) .toEqual({ diff --git a/src/server/saved_objects/client/lib/search_dsl/search_dsl.js b/src/server/saved_objects/client/lib/search_dsl/search_dsl.js index 69c127336d5a9..a76f91eb11b0b 100644 --- a/src/server/saved_objects/client/lib/search_dsl/search_dsl.js +++ b/src/server/saved_objects/client/lib/search_dsl/search_dsl.js @@ -6,14 +6,13 @@ import { getSortingParams } from './sorting_params'; export function getSearchDsl(mappings, options = {}) { const { type, - includeTypes, search, searchFields, sortField, sortOrder } = options; - if (!type && !includeTypes && sortField) { + if (!type && sortField) { throw Boom.notAcceptable('Cannot sort without filtering by type'); } @@ -22,7 +21,7 @@ export function getSearchDsl(mappings, options = {}) { } return { - ...getQueryParams(mappings, type, search, searchFields, includeTypes), + ...getQueryParams(mappings, type, search, searchFields), ...getSortingParams(mappings, type, sortField, sortOrder), }; } diff --git a/src/server/saved_objects/client/lib/search_dsl/search_dsl.test.js b/src/server/saved_objects/client/lib/search_dsl/search_dsl.test.js index a5f6765f4c3c1..e0de9ae4962ee 100644 --- a/src/server/saved_objects/client/lib/search_dsl/search_dsl.test.js +++ b/src/server/saved_objects/client/lib/search_dsl/search_dsl.test.js @@ -34,7 +34,6 @@ describe('getSearchDsl', () => { type: 'foo', search: 'bar', searchFields: ['baz'], - includeTypes: ['index-pattern', 'dashboard'] }; getSearchDsl(mappings, opts); @@ -45,7 +44,6 @@ describe('getSearchDsl', () => { opts.type, opts.search, opts.searchFields, - opts.includeTypes, ); }); diff --git a/src/server/saved_objects/client/lib/search_dsl/sorting_params.js b/src/server/saved_objects/client/lib/search_dsl/sorting_params.js index a76ddd769a23e..8a5af4af3d56b 100644 --- a/src/server/saved_objects/client/lib/search_dsl/sorting_params.js +++ b/src/server/saved_objects/client/lib/search_dsl/sorting_params.js @@ -7,21 +7,35 @@ export function getSortingParams(mappings, type, sortField, sortOrder) { return {}; } - const key = type ? `${type}.${sortField}` : sortField; + if (Array.isArray(type)) { + const rootField = getProperty(mappings, sortField); + if (!rootField) { + throw Boom.badRequest(`Unable to sort multiple types by field ${sortField}, not a root property`); + } + return { + sort: [{ + [sortField]: { + order: sortOrder, + unmapped_type: rootField.type + } + }] + }; + } + + + const key = `${type}.${sortField}`; const field = getProperty(mappings, key); if (!field) { throw Boom.badRequest(`Unknown sort field ${sortField}`); } return { - sort: [ - { - [key]: { - order: sortOrder, - unmapped_type: field.type - } + sort: [{ + [key]: { + order: sortOrder, + unmapped_type: field.type } - ] + }] }; } diff --git a/src/server/saved_objects/client/lib/search_dsl/sorting_params.test.js b/src/server/saved_objects/client/lib/search_dsl/sorting_params.test.js index cb342b47da35a..48c6510868119 100644 --- a/src/server/saved_objects/client/lib/search_dsl/sorting_params.test.js +++ b/src/server/saved_objects/client/lib/search_dsl/sorting_params.test.js @@ -3,10 +3,23 @@ import { getSortingParams } from './sorting_params'; const MAPPINGS = { rootType: { properties: { + type: { + type: 'text', + fields: { + raw: { + type: 'keyword' + } + } + }, pending: { properties: { title: { type: 'text', + fields: { + raw: { + type: 'keyword' + } + } } } }, @@ -55,8 +68,8 @@ describe('searchDsl/getSortParams', () => { }); }); - describe('search field no direction', () => { - describe('search field is simple property', () => { + describe('sortField no direction', () => { + describe('sortField is simple property with single type', () => { it('returns correct params', () => { expect(getSortingParams(MAPPINGS, 'saved', 'title')) .toEqual({ @@ -71,7 +84,27 @@ describe('searchDsl/getSortParams', () => { }); }); }); - describe('search field is multi-field', () => { + describe('sortField is simple root property with multiple types', () => { + it('returns correct params', () => { + expect(getSortingParams(MAPPINGS, ['saved', 'pending'], 'type')) + .toEqual({ + sort: [ + { + 'type': { + order: undefined, + unmapped_type: 'text' + } + } + ] + }); + }); + }); + describe('sortField is simple non-root property with multiple types', () => { + it('returns correct params', () => { + expect(() => getSortingParams(MAPPINGS, ['saved', 'pending'], 'title')).toThrowErrorMatchingSnapshot(); + }); + }); + describe('sortField is multi-field with single type', () => { it('returns correct params', () => { expect(getSortingParams(MAPPINGS, 'saved', 'title.raw')) .toEqual({ @@ -86,10 +119,30 @@ describe('searchDsl/getSortParams', () => { }); }); }); + describe('sortField is root multi-field with multiple types', () => { + it('returns correct params', () => { + expect(getSortingParams(MAPPINGS, ['saved', 'pending'], 'type.raw')) + .toEqual({ + sort: [ + { + 'type.raw': { + order: undefined, + unmapped_type: 'keyword' + } + } + ] + }); + }); + }); + describe('sortField is not-root multi-field with multiple types', () => { + it('returns correct params', () => { + expect(() => getSortingParams(MAPPINGS, ['saved', 'pending'], 'title.raw')).toThrowErrorMatchingSnapshot(); + }); + }); }); - describe('search with direction', () => { - describe('search field is simple property', () => { + describe('sort with direction', () => { + describe('sortField is simple property with single type', () => { it('returns correct params', () => { expect(getSortingParams(MAPPINGS, 'saved', 'title', 'desc')) .toEqual({ @@ -104,7 +157,27 @@ describe('searchDsl/getSortParams', () => { }); }); }); - describe('search field is multi-field', () => { + describe('sortField is root simple property with multiple type', () => { + it('returns correct params', () => { + expect(getSortingParams(MAPPINGS, ['saved', 'pending'], 'type', 'desc')) + .toEqual({ + sort: [ + { + 'type': { + order: 'desc', + unmapped_type: 'text' + } + } + ] + }); + }); + }); + describe('sortFields is non-root simple property with multiple types', () => { + it('returns correct params', () => { + expect(() => getSortingParams(MAPPINGS, ['saved', 'pending'], 'title', 'desc')).toThrowErrorMatchingSnapshot(); + }); + }); + describe('sortField is multi-field with single type', () => { it('returns correct params', () => { expect(getSortingParams(MAPPINGS, 'saved', 'title.raw', 'asc')) .toEqual({ @@ -119,5 +192,25 @@ describe('searchDsl/getSortParams', () => { }); }); }); + describe('sortField is root multi-field with multiple types', () => { + it('returns correct params', () => { + expect(getSortingParams(MAPPINGS, ['saved', 'pending'], 'type.raw', 'asc')) + .toEqual({ + sort: [ + { + 'type.raw': { + order: 'asc', + unmapped_type: 'keyword' + } + } + ] + }); + }); + }); + describe('sortField is non-root multi-field with multiple types', () => { + it('returns correct params', () => { + expect(() => getSortingParams(MAPPINGS, ['saved', 'pending'], 'title.raw', 'asc')).toThrowErrorMatchingSnapshot(); + }); + }); }); }); diff --git a/src/server/saved_objects/client/saved_objects_client.js b/src/server/saved_objects/client/saved_objects_client.js index a15d32c6aac59..fead7e5ff8eee 100644 --- a/src/server/saved_objects/client/saved_objects_client.js +++ b/src/server/saved_objects/client/saved_objects_client.js @@ -115,7 +115,7 @@ export class SavedObjectsClient { /** * @param {object} [options={}] - * @property {string} [options.type] + * @property {(string|Array)} [options.type] * @property {string} [options.search] * @property {Array} [options.searchFields] - see Elasticsearch Simple Query String * Query field argument for more information diff --git a/src/server/saved_objects/routes/find.js b/src/server/saved_objects/routes/find.js index 4c76a938ad347..32b3d5df2537d 100644 --- a/src/server/saved_objects/routes/find.js +++ b/src/server/saved_objects/routes/find.js @@ -10,8 +10,7 @@ export const createFindRoute = (prereqs) => ({ query: Joi.object().keys({ per_page: Joi.number().min(0).default(20), page: Joi.number().min(0).default(1), - type: Joi.string(), - include_types: Joi.array().items(Joi.string()).single(), + type: Joi.array().items(Joi.string()).single(), search: Joi.string().allow('').optional(), search_fields: Joi.array().items(Joi.string()).single(), sort_field: Joi.array().items(Joi.string()).single(), diff --git a/src/server/saved_objects/routes/find.test.js b/src/server/saved_objects/routes/find.test.js index 0a0ad5b10af14..1777ea157199e 100644 --- a/src/server/saved_objects/routes/find.test.js +++ b/src/server/saved_objects/routes/find.test.js @@ -131,7 +131,7 @@ describe('GET /api/saved_objects/_find', () => { }); }); - it('accepts the type as a query parameter', async () => { + it('accepts the query parameter type as a string', async () => { const request = { method: 'GET', url: '/api/saved_objects/_find?type=index-pattern' @@ -142,6 +142,20 @@ describe('GET /api/saved_objects/_find', () => { expect(savedObjectsClient.find.calledOnce).toBe(true); const options = savedObjectsClient.find.getCall(0).args[0]; - expect(options).toEqual({ perPage: 20, page: 1, type: 'index-pattern' }); + expect(options).toEqual({ perPage: 20, page: 1, type: [ 'index-pattern' ] }); + }); + + it('accepts the query parameter type as an array', async () => { + const request = { + method: 'GET', + url: '/api/saved_objects/_find?type=index-pattern&type=visualization' + }; + + await server.inject(request); + + expect(savedObjectsClient.find.calledOnce).toBe(true); + + const options = savedObjectsClient.find.getCall(0).args[0]; + expect(options).toEqual({ perPage: 20, page: 1, type: ['index-pattern', 'visualization'] }); }); });