Enable prettier for saved objects (#33194)

This commit is contained in:
Mike Côté 2019-03-13 18:35:20 -04:00 committed by GitHub
parent b90241cf45
commit 2fa3c01928
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1912 additions and 1680 deletions

View file

@ -67,6 +67,7 @@ module.exports = {
'packages/kbn-test-subj-selector/**/*',
'packages/kbn-test/**/*',
'packages/kbn-eslint-import-resolver-kibana/**/*',
'src/legacy/server/saved_objects/**/*',
'x-pack/plugins/apm/**/*',
'x-pack/plugins/canvas/**/*',
],

View file

@ -20,7 +20,11 @@
import { KibanaMigrator } from './migrations';
import { SavedObjectsSchema } from './schema';
import { SavedObjectsSerializer } from './serialization';
import { SavedObjectsClient, SavedObjectsRepository, ScopedSavedObjectsClientProvider } from './service';
import {
SavedObjectsClient,
SavedObjectsRepository,
ScopedSavedObjectsClientProvider,
} from './service';
import { getRootPropertiesObjects } from '../mappings';
import {
@ -41,7 +45,7 @@ export function savedObjectsMixin(kbnServer, server) {
server.decorate('server', 'kibanaMigrator', migrator);
const warn = (message) => server.log(['warning', 'saved-objects'], message);
const warn = message => server.log(['warning', 'saved-objects'], message);
// we use kibana.index which is technically defined in the kibana plugin, so if
// we don't have the plugin (mainly tests) we can't initialize the saved objects
if (!kbnServer.pluginSpecs.some(p => p.getId() === 'kibana')) {
@ -75,12 +79,12 @@ export function savedObjectsMixin(kbnServer, server) {
const visibleTypes = allTypes.filter(type => !schema.isHiddenType(type));
const createRepository = (callCluster, extraTypes = []) => {
if(typeof callCluster !== 'function') {
if (typeof callCluster !== 'function') {
throw new TypeError('Repository requires a "callCluster" function to be provided.');
}
// throw an exception if an extraType is not defined.
extraTypes.forEach(type => {
if(!allTypes.includes(type)) {
if (!allTypes.includes(type)) {
throw new Error(`Missing mappings for saved objects type '${type}'`);
}
});
@ -94,7 +98,7 @@ export function savedObjectsMixin(kbnServer, server) {
schema,
serializer,
allowedTypes,
callCluster
callCluster,
});
};
@ -123,7 +127,7 @@ export function savedObjectsMixin(kbnServer, server) {
server.decorate('server', 'savedObjects', service);
const savedObjectsClientCache = new WeakMap();
server.decorate('request', 'getSavedObjectsClient', function () {
server.decorate('request', 'getSavedObjectsClient', function() {
const request = this;
if (savedObjectsClientCache.has(request)) {

View file

@ -28,7 +28,7 @@ describe('Saved Objects Mixin', () => {
'kibana.index': 'kibana.index',
'savedObjects.maxImportExportSize': 10000,
};
const stubConfig = jest.fn((key) => {
const stubConfig = jest.fn(key => {
return config[key];
});
@ -57,12 +57,16 @@ describe('Saved Objects Mixin', () => {
mockKbnServer = {
server: mockServer,
ready: () => {},
pluginSpecs: { some: () => { return true; } },
pluginSpecs: {
some: () => {
return true;
},
},
uiExports: {
savedObjectSchemas: {
hiddentype: {
hidden: true,
}
},
},
savedObjectMappings: [
{
@ -95,7 +99,11 @@ describe('Saved Objects Mixin', () => {
mockKbnServer.pluginSpecs.some = () => false;
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.log).toHaveBeenCalledWith(expect.any(Array), expect.any(String));
expect(mockServer.decorate).toHaveBeenCalledWith('server', 'kibanaMigrator', expect.any(Object));
expect(mockServer.decorate).toHaveBeenCalledWith(
'server',
'kibanaMigrator',
expect.any(Object)
);
expect(mockServer.decorate).toHaveBeenCalledTimes(1);
expect(mockServer.route).not.toHaveBeenCalled();
});
@ -108,44 +116,66 @@ describe('Saved Objects Mixin', () => {
});
it('should add POST /api/saved_objects/_bulk_create', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_bulk_create', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/_bulk_create', method: 'POST' })
);
});
it('should add POST /api/saved_objects/_bulk_get', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_bulk_get', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/_bulk_get', method: 'POST' })
);
});
it('should add POST /api/saved_objects/{type}/{id?}', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/{type}/{id?}', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/{type}/{id?}', method: 'POST' })
);
});
it('should add DELETE /api/saved_objects/{type}/{id}', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'DELETE' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'DELETE' })
);
});
it('should add GET /api/saved_objects/_find', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_find', method: 'GET' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/_find', method: 'GET' })
);
});
it('should add GET /api/saved_objects/{type}/{id}', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'GET' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'GET' })
);
});
it('should add PUT /api/saved_objects/{type}/{id}', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'PUT' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/{type}/{id}', method: 'PUT' })
);
});
it('should add GET /api/saved_objects/_export', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_export', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/_export', method: 'POST' })
);
});
it('should add POST /api/saved_objects/_import', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_import', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({ path: '/api/saved_objects/_import', method: 'POST' })
);
});
it('should add POST /api/saved_objects/_resolve_import_errors', () => {
savedObjectsMixin(mockKbnServer, mockServer);
expect(mockServer.route)
.toHaveBeenCalledWith(expect.objectContaining({ path: '/api/saved_objects/_resolve_import_errors', method: 'POST' }));
expect(mockServer.route).toHaveBeenCalledWith(
expect.objectContaining({
path: '/api/saved_objects/_resolve_import_errors',
method: 'POST',
})
);
});
});
@ -154,7 +184,9 @@ describe('Saved Objects Mixin', () => {
beforeEach(() => {
savedObjectsMixin(mockKbnServer, mockServer);
const call = mockServer.decorate.mock.calls.filter(([objName, methodName]) => objName === 'server' && methodName === 'savedObjects');
const call = mockServer.decorate.mock.calls.filter(
([objName, methodName]) => objName === 'server' && methodName === 'savedObjects'
);
service = call[0][2];
});
@ -168,7 +200,7 @@ describe('Saved Objects Mixin', () => {
it('should not allow a repository with an undefined type', () => {
expect(() => {
service.getSavedObjectsRepository(mockCallEs, ['extraType']);
}).toThrow(new Error('Missing mappings for saved objects type \'extraType\''));
}).toThrow(new Error("Missing mappings for saved objects type 'extraType'"));
});
it('should create a repository without hidden types', () => {
@ -178,12 +210,19 @@ describe('Saved Objects Mixin', () => {
});
it('should create a repository with a unique list of allowed types', () => {
const repository = service.getSavedObjectsRepository(mockCallEs, ['config', 'config', 'config']);
const repository = service.getSavedObjectsRepository(mockCallEs, [
'config',
'config',
'config',
]);
expect(repository._allowedTypes).toEqual(['config', 'testtype']);
});
it('should create a repository with extraTypes minus duplicate', () => {
const repository = service.getSavedObjectsRepository(mockCallEs, ['hiddentype', 'hiddentype']);
const repository = service.getSavedObjectsRepository(mockCallEs, [
'hiddentype',
'hiddentype',
]);
expect(repository._allowedTypes).toEqual(['config', 'testtype', 'hiddentype']);
});
@ -221,7 +260,7 @@ describe('Saved Objects Mixin', () => {
});
it('should call underlining callCluster', async () => {
stubCallCluster.mockImplementation((method) => {
stubCallCluster.mockImplementation(method => {
if (method === 'indices.get') {
return { status: 404 };
} else if (method === 'indices.getAlias') {

View file

@ -30,7 +30,7 @@ const {
403: Forbidden,
413: RequestEntityTooLarge,
NotFound,
BadRequest
BadRequest,
} = elasticsearch.errors;
import {

View file

@ -65,7 +65,6 @@ export function isInvalidVersionError(error) {
return error && error[code] === CODE_INVALID_VERSION;
}
// 401 - Not Authorized
const CODE_NOT_AUTHORIZED = 'SavedObjectsClient/notAuthorized';
export function decorateNotAuthorizedError(error, reason) {
@ -75,7 +74,6 @@ export function isNotAuthorizedError(error) {
return error && error[code] === CODE_NOT_AUTHORIZED;
}
// 403 - Forbidden
const CODE_FORBIDDEN = 'SavedObjectsClient/forbidden';
export function decorateForbiddenError(error, reason) {
@ -85,7 +83,6 @@ export function isForbiddenError(error) {
return error && error[code] === CODE_FORBIDDEN;
}
// 413 - Request Entity Too Large
const CODE_REQUEST_ENTITY_TOO_LARGE = 'SavedObjectsClient/requestEntityTooLarge';
export function decorateRequestEntityTooLargeError(error, reason) {
@ -95,7 +92,6 @@ export function isRequestEntityTooLargeError(error) {
return error && error[code] === CODE_REQUEST_ENTITY_TOO_LARGE;
}
// 404 - Not Found
const CODE_NOT_FOUND = 'SavedObjectsClient/notFound';
export function createGenericNotFoundError(type = null, id = null) {
@ -108,7 +104,6 @@ export function isNotFoundError(error) {
return error && error[code] === CODE_NOT_FOUND;
}
// 409 - Conflict
const CODE_CONFLICT = 'SavedObjectsClient/conflict';
export function decorateConflictError(error, reason) {
@ -118,7 +113,6 @@ export function isConflictError(error) {
return error && error[code] === CODE_CONFLICT;
}
// 503 - Es Unavailable
const CODE_ES_UNAVAILABLE = 'SavedObjectsClient/esUnavailable';
export function decorateEsUnavailableError(error, reason) {
@ -128,7 +122,6 @@ export function isEsUnavailableError(error) {
return error && error[code] === CODE_ES_UNAVAILABLE;
}
// 503 - Unable to automatically create index because of action.auto_create_index setting
const CODE_ES_AUTO_CREATE_INDEX_ERROR = 'SavedObjectsClient/autoCreateIndex';
export function createEsAutoCreateIndexError() {
@ -141,7 +134,6 @@ export function isEsAutoCreateIndexError(error) {
return error && error[code] === CODE_ES_AUTO_CREATE_INDEX_ERROR;
}
// 500 - General Error
const CODE_GENERAL_ERROR = 'SavedObjectsClient/generalError';
export function decorateGeneralError(error, reason) {

View file

@ -45,18 +45,21 @@ describe('savedObjectsClient/errorTypes', () => {
const errorObj = createUnsupportedTypeError('someType');
it('should have the unsupported type message', () => {
expect(errorObj).toHaveProperty('message', 'Unsupported saved object type: \'someType\': Bad Request');
expect(errorObj).toHaveProperty(
'message',
"Unsupported saved object type: 'someType': Bad Request"
);
});
it('has boom properties', () => {
expect(errorObj.output.payload).toMatchObject({
statusCode: 400,
message: 'Unsupported saved object type: \'someType\': Bad Request',
message: "Unsupported saved object type: 'someType': Bad Request",
error: 'Bad Request',
});
});
it('should be identified by \'isBadRequestError\' method', () => {
it("should be identified by 'isBadRequestError' method", () => {
expect(isBadRequestError(errorObj)).toBeTruthy();
});
});
@ -67,7 +70,7 @@ describe('savedObjectsClient/errorTypes', () => {
expect(errorObj.message).toEqual('test reason message: Bad Request');
});
it('should be identified by \'isBadRequestError\' method', () => {
it("should be identified by 'isBadRequestError' method", () => {
expect(isBadRequestError(errorObj)).toBeTruthy();
});

View file

@ -31,7 +31,8 @@ export function includedFields(type, fields) {
const sourceFields = typeof fields === 'string' ? [fields] : fields;
const sourceType = type || '*';
return sourceFields.map(f => `${sourceType}.${f}`)
return sourceFields
.map(f => `${sourceType}.${f}`)
.concat('namespace')
.concat('type')
.concat(fields); // v5 compatibility

View file

@ -38,7 +38,7 @@ export class SavedObjectsRepository {
serializer,
migrator,
allowedTypes = [],
onBeforeWrite = () => { },
onBeforeWrite = () => {},
} = options;
// It's important that we migrate documents / mark them as up-to-date
@ -52,7 +52,7 @@ export class SavedObjectsRepository {
this._index = index;
this._mappings = mappings;
this._schema = schema;
if(allowedTypes.length === 0) {
if (allowedTypes.length === 0) {
throw new Error('Empty or missing types for saved object repository!');
}
this._allowedTypes = allowedTypes;
@ -78,17 +78,11 @@ export class SavedObjectsRepository {
* @property {string} [options.namespace]
* @property {array} [options.references] - [{ name, type, id }]
* @returns {promise} - { id, type, version, attributes }
*/
*/
async create(type, attributes = {}, options = {}) {
const {
id,
migrationVersion,
overwrite = false,
namespace,
references = [],
} = options;
const { id, migrationVersion, overwrite = false, namespace, references = [] } = options;
if(!this._isTypeAllowed(type)) {
if (!this._isTypeAllowed(type)) {
throw errors.createUnsupportedTypeError(type);
}
@ -139,22 +133,19 @@ export class SavedObjectsRepository {
* @returns {promise} - {saved_objects: [[{ id, type, version, references, attributes, error: { message } }]}
*/
async bulkCreate(objects, options = {}) {
const {
namespace,
overwrite = false,
} = options;
const { namespace, overwrite = false } = options;
const time = this._getCurrentTime();
const bulkCreateParams = [];
let requestIndexCounter = 0;
const expectedResults = objects.map((object) => {
if(!this._isTypeAllowed(object.type)) {
const expectedResults = objects.map(object => {
if (!this._isTypeAllowed(object.type)) {
return {
response: {
id: object.id,
type: object.type,
error: errors.createUnsupportedTypeError(object.type).output.payload,
}
},
};
}
@ -181,7 +172,7 @@ export class SavedObjectsRepository {
_id: expectedResult.rawMigratedDoc._id,
},
},
expectedResult.rawMigratedDoc._source,
expectedResult.rawMigratedDoc._source
);
return expectedResult;
@ -195,7 +186,7 @@ export class SavedObjectsRepository {
return {
saved_objects: expectedResults.map(expectedResult => {
if(expectedResult.response) {
if (expectedResult.response) {
return expectedResult.response;
}
@ -209,11 +200,7 @@ export class SavedObjectsRepository {
} = Object.values(response)[0];
const {
_source: {
type,
[type]: attributes,
references = [],
},
_source: { type, [type]: attributes, references = [] },
} = rawMigratedDoc;
const id = requestedId || responseId;
@ -222,15 +209,15 @@ export class SavedObjectsRepository {
return {
id,
type,
error: { statusCode: 409, message: 'version conflict, document already exists' }
error: { statusCode: 409, message: 'version conflict, document already exists' },
};
}
return {
id,
type,
error: {
message: error.reason || JSON.stringify(error)
}
message: error.reason || JSON.stringify(error),
},
};
}
@ -256,13 +243,11 @@ export class SavedObjectsRepository {
* @returns {promise}
*/
async delete(type, id, options = {}) {
if(!this._isTypeAllowed(type)) {
if (!this._isTypeAllowed(type)) {
throw errors.createGenericNotFoundError();
}
const {
namespace
} = options;
const { namespace } = options;
const response = await this._writeToCluster('delete', {
id: this._serializer.generateRawId(namespace, type, id),
@ -284,7 +269,7 @@ export class SavedObjectsRepository {
}
throw new Error(
`Unexpected Elasticsearch DELETE response: ${JSON.stringify({ type, id, response, })}`
`Unexpected Elasticsearch DELETE response: ${JSON.stringify({ type, id, response })}`
);
}
@ -312,8 +297,8 @@ export class SavedObjectsRepository {
...getSearchDsl(this._mappings, this._schema, {
namespace,
type: typesToDelete,
})
}
}),
},
};
return await this._writeToCluster('deleteByQuery', esOptions);
@ -354,23 +339,23 @@ export class SavedObjectsRepository {
throw new TypeError(`options.type must be a string or an array of strings`);
}
if(Array.isArray(type)) {
if (Array.isArray(type)) {
type = type.filter(type => this._isTypeAllowed(type));
if(type.length === 0) {
if (type.length === 0) {
return {
page,
per_page: perPage,
total: 0,
saved_objects: []
saved_objects: [],
};
}
}else{
if(!this._isTypeAllowed(type)) {
} else {
if (!this._isTypeAllowed(type)) {
return {
page,
per_page: perPage,
total: 0,
saved_objects: []
saved_objects: [],
};
}
}
@ -401,8 +386,8 @@ export class SavedObjectsRepository {
sortOrder,
namespace,
hasReference,
})
}
}),
},
};
const response = await this._callCluster('search', esOptions);
@ -414,7 +399,7 @@ export class SavedObjectsRepository {
page,
per_page: perPage,
total: 0,
saved_objects: []
saved_objects: [],
};
}
@ -441,9 +426,7 @@ export class SavedObjectsRepository {
* ])
*/
async bulkGet(objects = [], options = {}) {
const {
namespace
} = options;
const { namespace } = options;
if (objects.length === 0) {
return { saved_objects: [] };
@ -454,41 +437,47 @@ export class SavedObjectsRepository {
index: this._index,
body: {
docs: objects.reduce((acc, { type, id }) => {
if(this._isTypeAllowed(type)) {
if (this._isTypeAllowed(type)) {
acc.push({
_id: this._serializer.generateRawId(namespace, type, id),
});
}else{
unsupportedTypes.push({ id, type, error: errors.createUnsupportedTypeError(type).output.payload });
} else {
unsupportedTypes.push({
id,
type,
error: errors.createUnsupportedTypeError(type).output.payload,
});
}
return acc;
}, [])
}
}, []),
},
});
return {
saved_objects: response.docs.map((doc, i) => {
const { id, type } = objects[i];
saved_objects: response.docs
.map((doc, i) => {
const { id, type } = objects[i];
if (!doc.found) {
if (!doc.found) {
return {
id,
type,
error: { statusCode: 404, message: 'Not found' },
};
}
const time = doc._source.updated_at;
return {
id,
type,
error: { statusCode: 404, message: 'Not found' }
...(time && { updated_at: time }),
version: encodeHitVersion(doc),
attributes: doc._source[type],
references: doc._source.references || [],
migrationVersion: doc._source.migrationVersion,
};
}
const time = doc._source.updated_at;
return {
id,
type,
...time && { updated_at: time },
version: encodeHitVersion(doc),
attributes: doc._source[type],
references: doc._source.references || [],
migrationVersion: doc._source.migrationVersion,
};
}).concat(unsupportedTypes)
})
.concat(unsupportedTypes),
};
}
@ -502,18 +491,16 @@ export class SavedObjectsRepository {
* @returns {promise} - { id, type, version, attributes }
*/
async get(type, id, options = {}) {
if(!this._isTypeAllowed(type)) {
if (!this._isTypeAllowed(type)) {
throw errors.createGenericNotFoundError(type, id);
}
const {
namespace
} = options;
const { namespace } = options;
const response = await this._callCluster('get', {
id: this._serializer.generateRawId(namespace, type, id),
index: this._index,
ignore: [404]
ignore: [404],
});
const docNotFound = response.found === false;
@ -528,7 +515,7 @@ export class SavedObjectsRepository {
return {
id,
type,
...updatedAt && { updated_at: updatedAt },
...(updatedAt && { updated_at: updatedAt }),
version: encodeHitVersion(response),
attributes: response._source[type],
references: response._source.references || [],
@ -548,15 +535,11 @@ export class SavedObjectsRepository {
* @returns {promise}
*/
async update(type, id, attributes, options = {}) {
if(!this._isTypeAllowed(type)) {
if (!this._isTypeAllowed(type)) {
throw errors.createGenericNotFoundError(type, id);
}
const {
version,
namespace,
references = [],
} = options;
const { version, namespace, references = [] } = options;
const time = this._getCurrentTime();
const response = await this._writeToCluster('update', {
@ -570,7 +553,7 @@ export class SavedObjectsRepository {
[type]: attributes,
updated_at: time,
references,
}
},
},
});
@ -585,7 +568,7 @@ export class SavedObjectsRepository {
updated_at: time,
version: encodeHitVersion(response),
references,
attributes
attributes,
};
}
@ -606,18 +589,14 @@ export class SavedObjectsRepository {
if (typeof counterFieldName !== 'string') {
throw new Error('"counterFieldName" argument must be a string');
}
if(!this._isTypeAllowed(type)) {
if (!this._isTypeAllowed(type)) {
throw errors.createUnsupportedTypeError(type);
}
const {
migrationVersion,
namespace,
} = options;
const { migrationVersion, namespace } = options;
const time = this._getCurrentTime();
const migrated = this._migrator.migrateDocument({
id,
type,
@ -664,8 +643,6 @@ export class SavedObjectsRepository {
version: encodeHitVersion(response),
attributes: response.get._source[type],
};
}
async _writeToCluster(method, params) {
@ -700,8 +677,8 @@ export class SavedObjectsRepository {
_isTypeAllowed(types) {
const toCheck = [].concat(types);
for(const type of toCheck) {
if(!this._allowedTypes.includes(type)) {
for (const type of toCheck) {
if (!this._allowedTypes.includes(type)) {
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,12 +22,9 @@ import { PriorityCollection } from './priority_collection';
* Provider for the Scoped Saved Object Client.
*/
export class ScopedSavedObjectsClientProvider {
_wrapperFactories = new PriorityCollection();
constructor({
defaultClientFactory
}) {
constructor({ defaultClientFactory }) {
this._originalClientFactory = this._clientFactory = defaultClientFactory;
}

View file

@ -25,7 +25,7 @@ test(`uses default client factory when one isn't set`, () => {
const request = Symbol();
const clientProvider = new ScopedSavedObjectsClientProvider({
defaultClientFactory: defaultClientFactoryMock
defaultClientFactory: defaultClientFactoryMock,
});
const result = clientProvider.getClient(request);
@ -43,7 +43,7 @@ test(`uses custom client factory when one is set`, () => {
const customClientFactoryMock = jest.fn().mockReturnValue(returnValue);
const clientProvider = new ScopedSavedObjectsClientProvider({
defaultClientFactory: defaultClientFactoryMock
defaultClientFactory: defaultClientFactoryMock,
});
clientProvider.setClientFactory(customClientFactoryMock);
const result = clientProvider.getClient(request);
@ -58,9 +58,9 @@ test(`uses custom client factory when one is set`, () => {
test(`throws error when more than one scoped saved objects client factory is set`, () => {
const clientProvider = new ScopedSavedObjectsClientProvider({});
clientProvider.setClientFactory(() => { });
clientProvider.setClientFactory(() => {});
expect(() => {
clientProvider.setClientFactory(() => { });
clientProvider.setClientFactory(() => {});
}).toThrowErrorMatchingSnapshot();
});
@ -68,7 +68,7 @@ test(`invokes and uses wrappers in specified order`, () => {
const defaultClient = Symbol();
const defaultClientFactoryMock = jest.fn().mockReturnValue(defaultClient);
const clientProvider = new ScopedSavedObjectsClientProvider({
defaultClientFactory: defaultClientFactoryMock
defaultClientFactory: defaultClientFactoryMock,
});
const firstWrappedClient = Symbol('first client');
const firstClientWrapperFactoryMock = jest.fn().mockReturnValue(firstWrappedClient);
@ -83,10 +83,10 @@ test(`invokes and uses wrappers in specified order`, () => {
expect(actualClient).toBe(firstWrappedClient);
expect(firstClientWrapperFactoryMock).toHaveBeenCalledWith({
request,
client: secondWrapperClient
client: secondWrapperClient,
});
expect(secondClientWrapperFactoryMock).toHaveBeenCalledWith({
request,
client: defaultClient
client: defaultClient,
});
});

View file

@ -44,7 +44,6 @@ function getTypes(mappings, type) {
* @return {Object}
*/
function getFieldsForTypes(searchFields, types) {
if (!searchFields || !searchFields.length) {
return {
lenient: true,
@ -53,10 +52,10 @@ function getFieldsForTypes(searchFields, types) {
}
return {
fields: searchFields.reduce((acc, field) => [
...acc,
...types.map(prefix => `${prefix}.${field}`)
], []),
fields: searchFields.reduce(
(acc, field) => [...acc, ...types.map(prefix => `${prefix}.${field}`)],
[]
),
};
}
@ -76,19 +75,16 @@ function getClauseForType(schema, namespace, type) {
if (namespace && !schema.isNamespaceAgnostic(type)) {
return {
bool: {
must: [
{ term: { type } },
{ term: { namespace } },
]
}
must: [{ term: { type } }, { term: { namespace } }],
},
};
}
return {
bool: {
must: [{ term: { type } }],
must_not: [{ exists: { field: 'namespace' } }]
}
must_not: [{ exists: { field: 'namespace' } }],
},
};
}
@ -103,38 +99,51 @@ function getClauseForType(schema, namespace, type) {
* @param {Object} hasReference
* @return {Object}
*/
export function getQueryParams(mappings, schema, namespace, type, search, searchFields, defaultSearchOperator, hasReference) {
export function getQueryParams(
mappings,
schema,
namespace,
type,
search,
searchFields,
defaultSearchOperator,
hasReference
) {
const types = getTypes(mappings, type);
const bool = {
filter: [{
bool: {
must: hasReference
? [{
nested: {
path: 'references',
query: {
bool: {
must: [
{
term: {
'references.id': hasReference.id,
filter: [
{
bool: {
must: hasReference
? [
{
nested: {
path: 'references',
query: {
bool: {
must: [
{
term: {
'references.id': hasReference.id,
},
},
{
term: {
'references.type': hasReference.type,
},
},
],
},
},
{
term: {
'references.type': hasReference.type,
},
},
],
},
},
},
},
}]
: undefined,
should: types.map(type => getClauseForType(schema, namespace, type)),
minimum_should_match: 1
}
}],
]
: undefined,
should: types.map(type => getClauseForType(schema, namespace, type)),
minimum_should_match: 1,
},
},
],
};
if (search) {
@ -142,13 +151,10 @@ export function getQueryParams(mappings, schema, namespace, type, search, search
{
simple_query_string: {
query: search,
...getFieldsForTypes(
searchFields,
types
),
...getFieldsForTypes(searchFields, types),
...(defaultSearchOperator ? { default_operator: defaultSearchOperator } : {}),
}
}
},
},
];
}

View file

@ -43,7 +43,16 @@ export function getSearchDsl(mappings, schema, options = {}) {
}
return {
...getQueryParams(mappings, schema, namespace, type, search, searchFields, defaultSearchOperator, hasReference),
...getQueryParams(
mappings,
schema,
namespace,
type,
search,
searchFields,
defaultSearchOperator,
hasReference
),
...getSortingParams(mappings, type, sortField, sortOrder),
};
}

View file

@ -33,18 +33,26 @@ describe('getSearchDsl', () => {
describe('validation', () => {
it('throws when type is not specified', () => {
expect(() => {
getSearchDsl({}, {}, {
type: undefined,
sortField: 'title'
});
getSearchDsl(
{},
{},
{
type: undefined,
sortField: 'title',
}
);
}).toThrowError(/type must be specified/);
});
it('throws when sortOrder without sortField', () => {
expect(() => {
getSearchDsl({}, {}, {
type: 'foo',
sortOrder: 'desc'
});
getSearchDsl(
{},
{},
{
type: 'foo',
sortOrder: 'desc',
}
);
}).toThrowError(/sortOrder requires a sortField/);
});
});
@ -75,7 +83,7 @@ describe('getSearchDsl', () => {
opts.search,
opts.searchFields,
opts.defaultSearchOperator,
opts.hasReference,
opts.hasReference
);
});
@ -86,7 +94,7 @@ describe('getSearchDsl', () => {
const opts = {
type: 'foo',
sortField: 'bar',
sortOrder: 'baz'
sortOrder: 'baz',
};
getSearchDsl(mappings, schema, opts);
@ -95,7 +103,7 @@ describe('getSearchDsl', () => {
mappings,
opts.type,
opts.sortField,
opts.sortOrder,
opts.sortOrder
);
});

View file

@ -31,27 +31,33 @@ export function getSortingParams(mappings, type, sortField, sortOrder) {
if (TOP_LEVEL_FIELDS.includes(sortField)) {
return {
sort: [{
[sortField]: {
order: sortOrder,
sort: [
{
[sortField]: {
order: sortOrder,
},
},
}],
],
};
}
if (types.length > 1) {
const rootField = getProperty(mappings, sortField);
if (!rootField) {
throw Boom.badRequest(`Unable to sort multiple types by field ${sortField}, not a root property`);
throw Boom.badRequest(
`Unable to sort multiple types by field ${sortField}, not a root property`
);
}
return {
sort: [{
[sortField]: {
order: sortOrder,
unmapped_type: rootField.type
}
}]
sort: [
{
[sortField]: {
order: sortOrder,
unmapped_type: rootField.type,
},
},
],
};
}
@ -63,11 +69,13 @@ export function getSortingParams(mappings, type, sortField, sortOrder) {
}
return {
sort: [{
[key]: {
order: sortOrder,
unmapped_type: field.type
}
}]
sort: [
{
[key]: {
order: sortOrder,
unmapped_type: field.type,
},
},
],
};
}

View file

@ -25,9 +25,9 @@ const MAPPINGS = {
type: 'text',
fields: {
raw: {
type: 'keyword'
}
}
type: 'keyword',
},
},
},
pending: {
properties: {
@ -35,11 +35,11 @@ const MAPPINGS = {
type: 'text',
fields: {
raw: {
type: 'keyword'
}
}
}
}
type: 'keyword',
},
},
},
},
},
saved: {
properties: {
@ -47,128 +47,124 @@ const MAPPINGS = {
type: 'text',
fields: {
raw: {
type: 'keyword'
}
}
type: 'keyword',
},
},
},
obj: {
properties: {
key1: {
type: 'text'
}
}
}
}
}
}
type: 'text',
},
},
},
},
},
},
};
describe('searchDsl/getSortParams', () => {
describe('no sortField, type, or order', () => {
it('returns no params', () => {
expect(getSortingParams(MAPPINGS))
.toEqual({});
expect(getSortingParams(MAPPINGS)).toEqual({});
});
});
describe('type, no sortField', () => {
it('returns no params', () => {
expect(getSortingParams(MAPPINGS, 'pending'))
.toEqual({});
expect(getSortingParams(MAPPINGS, 'pending')).toEqual({});
});
});
describe('type, order, no sortField', () => {
it('returns no params', () => {
expect(getSortingParams(MAPPINGS, 'saved', null, 'desc'))
.toEqual({});
expect(getSortingParams(MAPPINGS, 'saved', null, 'desc')).toEqual({});
});
});
describe('sortField no direction', () => {
describe('sortField is simple property with single type', () => {
it('returns correct params', () => {
expect(getSortingParams(MAPPINGS, 'saved', 'title'))
.toEqual({
sort: [
{
'saved.title': {
order: undefined,
unmapped_type: 'text'
}
}
]
});
expect(getSortingParams(MAPPINGS, 'saved', 'title')).toEqual({
sort: [
{
'saved.title': {
order: undefined,
unmapped_type: 'text',
},
},
],
});
});
});
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'
}
}
]
});
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();
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({
sort: [
{
'saved.title.raw': {
order: undefined,
unmapped_type: 'keyword'
}
}
]
});
expect(getSortingParams(MAPPINGS, 'saved', 'title.raw')).toEqual({
sort: [
{
'saved.title.raw': {
order: undefined,
unmapped_type: 'keyword',
},
},
],
});
});
});
describe('sortField is multi-field with single type as array', () => {
it('returns correct params', () => {
expect(getSortingParams(MAPPINGS, ['saved'], 'title.raw'))
.toEqual({
sort: [
{
'saved.title.raw': {
order: undefined,
unmapped_type: 'keyword'
}
}
]
});
expect(getSortingParams(MAPPINGS, ['saved'], 'title.raw')).toEqual({
sort: [
{
'saved.title.raw': {
order: undefined,
unmapped_type: 'keyword',
},
},
],
});
});
});
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'
}
}
]
});
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();
expect(() =>
getSortingParams(MAPPINGS, ['saved', 'pending'], 'title.raw')
).toThrowErrorMatchingSnapshot();
});
});
});
@ -176,72 +172,72 @@ describe('searchDsl/getSortParams', () => {
describe('sort with direction', () => {
describe('sortField is simple property with single type', () => {
it('returns correct params', () => {
expect(getSortingParams(MAPPINGS, 'saved', 'title', 'desc'))
.toEqual({
sort: [
{
'saved.title': {
order: 'desc',
unmapped_type: 'text'
}
}
]
});
expect(getSortingParams(MAPPINGS, 'saved', 'title', 'desc')).toEqual({
sort: [
{
'saved.title': {
order: 'desc',
unmapped_type: 'text',
},
},
],
});
});
});
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'
}
}
]
});
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();
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({
sort: [
{
'saved.title.raw': {
order: 'asc',
unmapped_type: 'keyword'
}
}
]
});
expect(getSortingParams(MAPPINGS, 'saved', 'title.raw', 'asc')).toEqual({
sort: [
{
'saved.title.raw': {
order: 'asc',
unmapped_type: 'keyword',
},
},
],
});
});
});
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'
}
}
]
});
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();
expect(() =>
getSortingParams(MAPPINGS, ['saved', 'pending'], 'title.raw', 'asc')
).toThrowErrorMatchingSnapshot();
});
});
});

View file

@ -17,9 +17,7 @@
* under the License.
*/
import {
errors,
} from './lib';
import { errors } from './lib';
export class SavedObjectsClient {
constructor(repository) {
@ -91,8 +89,8 @@ export class SavedObjectsClient {
*
* @type {ErrorHelpers} see ./lib/errors
*/
static errors = errors
errors = errors
static errors = errors;
errors = errors;
/**
* Persists an object
@ -106,7 +104,7 @@ export class SavedObjectsClient {
* @property {string} [options.namespace]
* @property {array} [options.references] - [{ name, type, id }]
* @returns {promise} - { id, type, version, attributes }
*/
*/
async create(type, attributes = {}, options = {}) {
return this._repository.create(type, attributes, options);
}