mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Change can* signature to be the same as their equivalent function, apply PR feedback
This commit is contained in:
parent
6287a8cecf
commit
b657ac8fc1
15 changed files with 410 additions and 130 deletions
|
@ -36,15 +36,16 @@ describe('getSortedObjectsForExport()', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canBulkGet.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkGet.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((options: any) => {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map((type: string) => ({ type, can: true }));
|
||||
});
|
||||
});
|
||||
|
||||
test('exports selected types and sorts them', async () => {
|
||||
|
|
|
@ -74,7 +74,7 @@ async function fetchObjectsToExport({
|
|||
'bulk_get',
|
||||
objectTypes,
|
||||
supportedTypes,
|
||||
await savedObjectsClient.canBulkGet(objectTypes)
|
||||
await savedObjectsClient.canBulkGet(objects)
|
||||
);
|
||||
if (objects.length > exportSizeLimit) {
|
||||
throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`);
|
||||
|
@ -95,7 +95,12 @@ async function fetchObjectsToExport({
|
|||
'find',
|
||||
types || [],
|
||||
supportedTypes,
|
||||
await savedObjectsClient.canFind(types || [])
|
||||
await savedObjectsClient.canFind({
|
||||
type: types,
|
||||
sortField: '_id',
|
||||
sortOrder: 'asc',
|
||||
perPage: exportSizeLimit,
|
||||
})
|
||||
);
|
||||
const findResponse = await savedObjectsClient.find({
|
||||
type: types,
|
||||
|
|
|
@ -72,15 +72,16 @@ describe('importSavedObjects()', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canBulkGet.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkGet.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((options: any) => {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map((type: string) => ({ type, can: true }));
|
||||
});
|
||||
});
|
||||
|
||||
test('returns early when no objects exist', async () => {
|
||||
|
|
|
@ -52,7 +52,7 @@ export async function importSavedObjects({
|
|||
const objectsFromStream = await collectSavedObjects({ readStream, objectLimit });
|
||||
|
||||
const objectTypes = [...new Set(objectsFromStream.map(obj => obj.type))];
|
||||
const authorizedTypes = await savedObjectsClient.canBulkCreate(objectTypes);
|
||||
const authorizedTypes = await savedObjectsClient.canBulkCreate(objectsFromStream, { overwrite });
|
||||
const invalidTypes = [
|
||||
...new Set([
|
||||
...objectTypes.filter(type => !supportedTypes.includes(type)),
|
||||
|
|
|
@ -78,15 +78,16 @@ describe('resolveImportErrors()', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canBulkGet.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkGet.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((options: any) => {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map((type: string) => ({ type, can: true }));
|
||||
});
|
||||
});
|
||||
|
||||
test('works with empty parameters', async () => {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import Boom from 'boom';
|
||||
import { Readable } from 'stream';
|
||||
import { SavedObjectsClient } from '../service';
|
||||
import { SavedObjectsClient, SavedObject } from '../service';
|
||||
import { collectSavedObjects } from './collect_saved_objects';
|
||||
import { createObjectsFilter } from './create_objects_filter';
|
||||
import { extractErrors } from './extract_errors';
|
||||
|
@ -41,6 +41,36 @@ interface ImportResponse {
|
|||
errors?: ImportError[];
|
||||
}
|
||||
|
||||
async function getAuthorizedTypes(
|
||||
savedObjectsClient: SavedObjectsClient,
|
||||
objects: SavedObject[],
|
||||
retries: Retry[]
|
||||
) {
|
||||
// Call canBulkCreate twice since the parameters have to be the same as if bulkCreate is called
|
||||
const { objectsToOverwrite, objectsToNotOverwrite } = splitOverwrites(objects, retries);
|
||||
const resultForObjectsToOverwrite = await savedObjectsClient.canBulkCreate(objectsToOverwrite, {
|
||||
overwrite: true,
|
||||
});
|
||||
const resultForObjectsToNotOverwrite = await savedObjectsClient.canBulkCreate(
|
||||
objectsToNotOverwrite
|
||||
);
|
||||
|
||||
// Extract which types can be created in bulk
|
||||
const resultMap: { [key: string]: boolean } = {};
|
||||
for (const { type, can } of resultForObjectsToNotOverwrite) {
|
||||
resultMap[type] = can;
|
||||
}
|
||||
for (const { type, can } of resultForObjectsToOverwrite) {
|
||||
const canPreviously = resultMap[type] !== false;
|
||||
resultMap[type] = canPreviously && can;
|
||||
}
|
||||
|
||||
return Object.keys(resultMap).map(type => ({
|
||||
type,
|
||||
can: resultMap[type],
|
||||
}));
|
||||
}
|
||||
|
||||
export async function resolveImportErrors({
|
||||
readStream,
|
||||
objectLimit,
|
||||
|
@ -60,7 +90,7 @@ export async function resolveImportErrors({
|
|||
});
|
||||
|
||||
const objectTypes = [...new Set(objectsToResolve.map(obj => obj.type))];
|
||||
const authorizedTypes = await savedObjectsClient.canBulkCreate(objectTypes);
|
||||
const authorizedTypes = await getAuthorizedTypes(savedObjectsClient, objectsToResolve, retries);
|
||||
const invalidTypes = [
|
||||
...new Set([
|
||||
...objectTypes.filter(type => !supportedTypes.includes(type)),
|
||||
|
|
|
@ -40,15 +40,16 @@ describe('POST /api/saved_objects/_import', () => {
|
|||
beforeEach(() => {
|
||||
server = createMockServer();
|
||||
jest.resetAllMocks();
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canBulkGet.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkGet.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((options: any) => {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map((type: string) => ({ type, can: true }));
|
||||
});
|
||||
|
||||
const prereqs = {
|
||||
getSavedObjectsClient: {
|
||||
|
|
|
@ -40,15 +40,16 @@ describe('POST /api/saved_objects/_resolve_import_errors', () => {
|
|||
beforeEach(() => {
|
||||
server = createMockServer();
|
||||
jest.resetAllMocks();
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkCreate.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canBulkGet.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((types: string[]) =>
|
||||
types.map(type => ({ type, can: true }))
|
||||
savedObjectsClient.canBulkGet.mockImplementation((objects: any, options: any) =>
|
||||
objects.map((obj: any) => ({ type: obj.type, can: true }))
|
||||
);
|
||||
savedObjectsClient.canFind.mockImplementation((options: any) => {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map((type: string) => ({ type, can: true }));
|
||||
});
|
||||
|
||||
const prereqs = {
|
||||
getSavedObjectsClient: {
|
||||
|
|
|
@ -127,8 +127,9 @@ export declare class SavedObjectsClient {
|
|||
objects: Array<BulkCreateObject<T>>,
|
||||
options?: CreateOptions
|
||||
): Promise<BulkCreateResponse<T>>;
|
||||
public canBulkCreate(
|
||||
types: string[]
|
||||
public canBulkCreate<T extends SavedObjectAttributes = any>(
|
||||
objects: Array<BulkCreateObject<T>>,
|
||||
options?: CreateOptions
|
||||
): Promise<
|
||||
Array<{
|
||||
type: string;
|
||||
|
@ -139,12 +140,17 @@ export declare class SavedObjectsClient {
|
|||
public find<T extends SavedObjectAttributes = any>(
|
||||
options: FindOptions
|
||||
): Promise<FindResponse<T>>;
|
||||
public canFind(types: string[]): Promise<Array<{ type: string; can: boolean }>>;
|
||||
public canFind<T extends SavedObjectAttributes = any>(
|
||||
options: FindOptions
|
||||
): Promise<Array<{ type: string; can: boolean }>>;
|
||||
public bulkGet<T extends SavedObjectAttributes = any>(
|
||||
objects: BulkGetObjects,
|
||||
options?: BaseOptions
|
||||
): Promise<BulkGetResponse<T>>;
|
||||
public canBulkGet(types: string[]): Promise<Array<{ type: string; can: boolean }>>;
|
||||
public canBulkGet<T extends SavedObjectAttributes = any>(
|
||||
objects: BulkGetObjects,
|
||||
options?: BaseOptions
|
||||
): Promise<Array<{ type: string; can: boolean }>>;
|
||||
public get<T extends SavedObjectAttributes = any>(
|
||||
type: string,
|
||||
id: string,
|
||||
|
|
|
@ -127,11 +127,15 @@ export class SavedObjectsClient {
|
|||
*
|
||||
* This should only be used by import / export / resolve import errors.
|
||||
*
|
||||
* @param {Array<string>} types Types of saved objects
|
||||
* @param {array} objects - [{ type, id, attributes }]
|
||||
* @param {object} [options={}]
|
||||
* @property {boolean} [options.overwrite=false] - overwrites existing documents
|
||||
* @property {string} [options.namespace]
|
||||
* @return [{ type, can }]
|
||||
*/
|
||||
async canBulkCreate(types) {
|
||||
return types.map(type => ({ type, can: true }));
|
||||
async canBulkCreate(objects) {
|
||||
const types = new Set(objects.map(obj => obj.type));
|
||||
return [...types].map(type => ({ type, can: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,17 +176,30 @@ export class SavedObjectsClient {
|
|||
*
|
||||
* This should only be used by import / export / resolve import errors.
|
||||
*
|
||||
* @param {Array<string>} types Types of saved objects
|
||||
* @param {object} [options={}]
|
||||
* @property {(string|Array<string>)} [options.type]
|
||||
* @property {string} [options.search]
|
||||
* @property {string} [options.defaultSearchOperator]
|
||||
* @property {Array<string>} [options.searchFields] - see Elasticsearch Simple Query String
|
||||
* Query field argument for more information
|
||||
* @property {integer} [options.page=1]
|
||||
* @property {integer} [options.perPage=20]
|
||||
* @property {string} [options.sortField]
|
||||
* @property {string} [options.sortOrder]
|
||||
* @property {Array<string>} [options.fields]
|
||||
* @property {string} [options.namespace]
|
||||
* @property {object} [options.hasReference] - { type, id }
|
||||
* @return [{ type, can }]
|
||||
*/
|
||||
async canFind(types) {
|
||||
async canFind(options = {}) {
|
||||
const types = Array.isArray(options.type) ? options.type : [options.type];
|
||||
return types.map(type => ({ type, can: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of objects by id
|
||||
*
|
||||
* @param {array} objects - an array ids, or an array of objects containing id and optionally type
|
||||
* @param {array} objects - an array of objects containing id and type
|
||||
* @param {object} [options={}]
|
||||
* @property {string} [options.namespace]
|
||||
* @returns {promise} - { saved_objects: [{ id, type, version, attributes }] }
|
||||
|
@ -202,11 +219,14 @@ export class SavedObjectsClient {
|
|||
*
|
||||
* This should only be used by import / export / resolve import errors.
|
||||
*
|
||||
* @param {Array<string>} types Types of saved objects
|
||||
* @param {array} objects - an array of objects containing id and type
|
||||
* @param {object} [options={}]
|
||||
* @property {string} [options.namespace]
|
||||
* @return [{ type, can }]
|
||||
*/
|
||||
async canBulkGet(types) {
|
||||
return types.map(type => ({ type, can: true }));
|
||||
async canBulkGet(objects = []) {
|
||||
const types = new Set(objects.map(obj => obj.type));
|
||||
return [...types].map(type => ({ type, can: true }));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,8 +48,9 @@ export class SecureSavedObjectsClientWrapper {
|
|||
return await this._baseClient.bulkCreate(objects, options);
|
||||
}
|
||||
|
||||
async canBulkCreate(types) {
|
||||
return await this._checkAuthorizedTypes(types, 'bulk_create');
|
||||
async canBulkCreate(objects, options = {}) {
|
||||
const types = uniq(objects.map(o => o.type));
|
||||
return await this._checkSavedObjectPrivileges(types, 'bulk_create', { objects, options });
|
||||
}
|
||||
|
||||
async delete(type, id, options) {
|
||||
|
@ -72,8 +73,8 @@ export class SecureSavedObjectsClientWrapper {
|
|||
return this._baseClient.find(options);
|
||||
}
|
||||
|
||||
async canFind(types) {
|
||||
return await this._checkAuthorizedTypes(types, 'find');
|
||||
async canFind(options = {}) {
|
||||
return await this._checkSavedObjectPrivileges(options.type, 'find', { options });
|
||||
}
|
||||
|
||||
async bulkGet(objects = [], options = {}) {
|
||||
|
@ -87,8 +88,9 @@ export class SecureSavedObjectsClientWrapper {
|
|||
return await this._baseClient.bulkGet(objects, options);
|
||||
}
|
||||
|
||||
async canBulkGet(types) {
|
||||
return await this._checkAuthorizedTypes(types, 'bulk_get');
|
||||
async canBulkGet(objects = [], options = {}) {
|
||||
const types = uniq(objects.map(o => o.type));
|
||||
return await this._checkSavedObjectPrivileges(types, 'bulk_get', { objects, options });
|
||||
}
|
||||
|
||||
async get(type, id, options = {}) {
|
||||
|
@ -111,46 +113,48 @@ export class SecureSavedObjectsClientWrapper {
|
|||
return await this._baseClient.update(type, id, attributes, options);
|
||||
}
|
||||
|
||||
async _checkSavedObjectPrivileges(actions) {
|
||||
async _checkSavedObjectPrivileges(typeOrTypes, action, args) {
|
||||
const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
||||
const actionsToTypesMap = new Map(types.map(type => [this._actions.savedObject.get(type, action), type]));
|
||||
const actions = Array.from(actionsToTypesMap.keys());
|
||||
|
||||
// Check privileges
|
||||
let result;
|
||||
try {
|
||||
return await this._checkPrivileges(actions);
|
||||
result = await this._checkPrivileges(actions);
|
||||
} catch(error) {
|
||||
const { reason } = get(error, 'body.error', {});
|
||||
throw this.errors.decorateGeneralError(error, reason);
|
||||
}
|
||||
}
|
||||
|
||||
async _checkAuthorizedTypes(typeOrTypes, action) {
|
||||
const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
||||
const actionsToTypesMap = new Map(types.map(type => [this._actions.savedObject.get(type, action), type]));
|
||||
const actions = Array.from(actionsToTypesMap.keys());
|
||||
const { privileges } = await this._checkSavedObjectPrivileges(actions);
|
||||
const missingPrivileges = this._getMissingPrivileges(privileges);
|
||||
const invalidTypes = missingPrivileges.map(privilege => actionsToTypesMap.get(privilege));
|
||||
return types.map(type => ({
|
||||
type,
|
||||
can: !invalidTypes.includes(type),
|
||||
}));
|
||||
}
|
||||
|
||||
async _ensureAuthorized(typeOrTypes, action, args) {
|
||||
const types = Array.isArray(typeOrTypes) ? typeOrTypes : [typeOrTypes];
|
||||
const actionsToTypesMap = new Map(types.map(type => [this._actions.savedObject.get(type, action), type]));
|
||||
const actions = Array.from(actionsToTypesMap.keys());
|
||||
const { hasAllRequested, username, privileges } = await this._checkSavedObjectPrivileges(actions);
|
||||
|
||||
if (hasAllRequested) {
|
||||
this._auditLogger.savedObjectsAuthorizationSuccess(username, action, types, args);
|
||||
// Log to audit and return array of types that are authorized
|
||||
let typesMissingPrivileges = [];
|
||||
if (result.hasAllRequested) {
|
||||
this._auditLogger.savedObjectsAuthorizationSuccess(result.username, action, types, args);
|
||||
} else {
|
||||
const missingPrivileges = this._getMissingPrivileges(privileges);
|
||||
const missingPrivileges = this._getMissingPrivileges(result.privileges);
|
||||
this._auditLogger.savedObjectsAuthorizationFailure(
|
||||
username,
|
||||
result.username,
|
||||
action,
|
||||
types,
|
||||
missingPrivileges,
|
||||
args
|
||||
);
|
||||
const msg = `Unable to ${action} ${missingPrivileges.map(privilege => actionsToTypesMap.get(privilege)).sort().join(',')}`;
|
||||
typesMissingPrivileges = missingPrivileges.map(privilege => actionsToTypesMap.get(privilege));
|
||||
}
|
||||
return types.map(type => ({
|
||||
type,
|
||||
can: !typesMissingPrivileges.includes(type),
|
||||
}));
|
||||
}
|
||||
|
||||
async _ensureAuthorized(typeOrTypes, action, args) {
|
||||
const result = await this._checkSavedObjectPrivileges(typeOrTypes, action, args);
|
||||
const unauthorizedTypes = result
|
||||
.filter(obj => obj.can === false)
|
||||
.map(obj => obj.type);
|
||||
if (unauthorizedTypes.length) {
|
||||
const msg = `Unable to ${action} ${unauthorizedTypes.sort().join(',')}`;
|
||||
throw this.errors.decorateForbiddenError(new Error(msg));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -332,7 +332,7 @@ describe(`spaces disabled`, () => {
|
|||
spaces: null,
|
||||
});
|
||||
|
||||
await expect(client.canBulkCreate([type])).rejects.toThrowError(mockErrors.generalError);
|
||||
await expect(client.canBulkCreate([{ type }])).rejects.toThrowError(mockErrors.generalError);
|
||||
|
||||
expect(mockCheckPrivilegesDynamicallyWithRequest).toHaveBeenCalledWith(mockRequest);
|
||||
expect(mockCheckPrivileges).toHaveBeenCalledWith([mockActions.savedObject.get(type, 'bulk_create')]);
|
||||
|
@ -368,9 +368,14 @@ describe(`spaces disabled`, () => {
|
|||
savedObjectTypes: [],
|
||||
spaces: null,
|
||||
});
|
||||
const types = [type1, type2 ];
|
||||
const objects = [
|
||||
{ type: type1 },
|
||||
{ type: type1 },
|
||||
{ type: type2 },
|
||||
];
|
||||
const options = Symbol();
|
||||
|
||||
const result = await client.canBulkCreate(types);
|
||||
const result = await client.canBulkCreate(objects, options);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: type1,
|
||||
|
@ -387,6 +392,16 @@ describe(`spaces disabled`, () => {
|
|||
mockActions.savedObject.get(type1, 'bulk_create'),
|
||||
mockActions.savedObject.get(type2, 'bulk_create'),
|
||||
]);
|
||||
expect(mockAuditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledWith(
|
||||
username,
|
||||
'bulk_create',
|
||||
[type1, type2],
|
||||
[mockActions.savedObject.get(type1, 'bulk_create')],
|
||||
{
|
||||
objects,
|
||||
options,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -702,7 +717,7 @@ describe(`spaces disabled`, () => {
|
|||
spaces: null,
|
||||
});
|
||||
|
||||
await expect(client.canFind([type])).rejects.toThrowError(mockErrors.generalError);
|
||||
await expect(client.canFind({ type })).rejects.toThrowError(mockErrors.generalError);
|
||||
|
||||
expect(mockCheckPrivilegesDynamicallyWithRequest).toHaveBeenCalledWith(mockRequest);
|
||||
expect(mockCheckPrivileges).toHaveBeenCalledWith([mockActions.savedObject.get(type, 'find')]);
|
||||
|
@ -739,9 +754,9 @@ describe(`spaces disabled`, () => {
|
|||
savedObjectTypes: [],
|
||||
spaces: null,
|
||||
});
|
||||
const types = [type1, type2];
|
||||
const result = await client.canFind(types);
|
||||
const options = { type: [type1, type2] };
|
||||
|
||||
const result = await client.canFind(options);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: type1,
|
||||
|
@ -752,11 +767,22 @@ describe(`spaces disabled`, () => {
|
|||
can: true,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockCheckPrivilegesDynamicallyWithRequest).toHaveBeenCalledWith(mockRequest);
|
||||
expect(mockCheckPrivileges).toHaveBeenCalledWith([
|
||||
mockActions.savedObject.get(type1, 'find'),
|
||||
mockActions.savedObject.get(type2, 'find')
|
||||
]);
|
||||
expect(mockAuditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledWith(
|
||||
username,
|
||||
'find',
|
||||
[type1, type2],
|
||||
[mockActions.savedObject.get(type1, 'find')],
|
||||
{
|
||||
options
|
||||
}
|
||||
);
|
||||
expect(mockAuditLogger.savedObjectsAuthorizationSuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -921,7 +947,7 @@ describe(`spaces disabled`, () => {
|
|||
spaces: null,
|
||||
});
|
||||
|
||||
await expect(client.canBulkGet([type])).rejects.toThrowError(mockErrors.generalError);
|
||||
await expect(client.canBulkGet([{ type }])).rejects.toThrowError(mockErrors.generalError);
|
||||
|
||||
expect(mockCheckPrivilegesDynamicallyWithRequest).toHaveBeenCalledWith(mockRequest);
|
||||
expect(mockCheckPrivileges).toHaveBeenCalledWith([mockActions.savedObject.get(type, 'bulk_get')]);
|
||||
|
@ -957,10 +983,14 @@ describe(`spaces disabled`, () => {
|
|||
savedObjectTypes: [],
|
||||
spaces: null,
|
||||
});
|
||||
const types = [type1, type2];
|
||||
|
||||
const result = await client.canBulkGet(types);
|
||||
const objects = [
|
||||
{ type: type1 },
|
||||
{ type: type1 },
|
||||
{ type: type2 },
|
||||
];
|
||||
const options = Symbol();
|
||||
|
||||
const result = await client.canBulkGet(objects, options);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
type: type1,
|
||||
|
@ -971,11 +1001,23 @@ describe(`spaces disabled`, () => {
|
|||
can: true,
|
||||
},
|
||||
]);
|
||||
|
||||
expect(mockCheckPrivilegesDynamicallyWithRequest).toHaveBeenCalledWith(mockRequest);
|
||||
expect(mockCheckPrivileges).toHaveBeenCalledWith([
|
||||
mockActions.savedObject.get(type1, 'bulk_get'),
|
||||
mockActions.savedObject.get(type2, 'bulk_get'),
|
||||
]);
|
||||
expect(mockAuditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledWith(
|
||||
username,
|
||||
'bulk_get',
|
||||
[type1, type2],
|
||||
[mockActions.savedObject.get(type1, 'bulk_get')],
|
||||
{
|
||||
objects,
|
||||
options,
|
||||
}
|
||||
);
|
||||
expect(mockAuditLogger.savedObjectsAuthorizationSuccess).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,11 +8,21 @@ exports[`default space #bulkGet throws error if objects type is space 1`] = `"Sp
|
|||
|
||||
exports[`default space #bulkGet throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`default space #canBulkCreate throws error if types contains space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
exports[`default space #canBulkCreate throws error if objects type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`default space #canBulkCreate throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`default space #canBulkGet throws error if objects type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`default space #canFind throws error if types contains space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
exports[`default space #canBulkGet throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`default space #canFind if options.type isn't provided specifies options.type based on the types excluding the space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`default space #canFind throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`default space #canFind throws error if options.type is array containing space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`default space #canFind throws error if options.type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`default space #create throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
|
@ -46,11 +56,21 @@ exports[`space_1 space #bulkGet throws error if objects type is space 1`] = `"Sp
|
|||
|
||||
exports[`space_1 space #bulkGet throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`space_1 space #canBulkCreate throws error if types contains space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
exports[`space_1 space #canBulkCreate throws error if objects type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`space_1 space #canBulkCreate throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`space_1 space #canBulkGet throws error if objects type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`space_1 space #canFind throws error if types contains space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
exports[`space_1 space #canBulkGet throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`space_1 space #canFind if options.type isn't provided specifies options.type based on the types excluding the space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`space_1 space #canFind throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
exports[`space_1 space #canFind throws error if options.type is array containing space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`space_1 space #canFind throws error if options.type is space 1`] = `"Spaces can not be accessed using the SavedObjectsClient"`;
|
||||
|
||||
exports[`space_1 space #create throws error if options.namespace is specified 1`] = `"Spaces currently determines the namespaces"`;
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ const createMockClient = () => {
|
|||
});
|
||||
|
||||
describe('#canBulkGet', () => {
|
||||
test('throws error if objects type is space', async () => {
|
||||
test(`throws error if options.namespace is specified`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const spacesService = createSpacesService(server);
|
||||
|
@ -185,10 +185,31 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
await expect(client.canBulkGet(['foo', 'space'])).rejects.toThrowErrorMatchingSnapshot();
|
||||
await expect(
|
||||
client.canBulkGet([{ id: '', type: 'foo' }], { namespace: 'bar' })
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('supplements options', async () => {
|
||||
test(`throws error if objects type is space`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.canBulkGet([{ id: '', type: 'foo' }, { id: '', type: 'space' }], {
|
||||
namespace: 'bar',
|
||||
})
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test(`supplements options with undefined namespace`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
|
@ -202,12 +223,16 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
const reqTypes = Object.freeze(['foo']);
|
||||
const objects = [{ type: 'foo' }];
|
||||
const options = Object.freeze({ foo: 'bar' });
|
||||
// @ts-ignore
|
||||
const actualReturnValue = await client.canBulkGet(reqTypes);
|
||||
const actualReturnValue = await client.canBulkGet(objects, options);
|
||||
|
||||
expect(actualReturnValue).toBe(expectedReturnValue);
|
||||
expect(baseClient.canBulkGet).toHaveBeenCalledWith(reqTypes);
|
||||
expect(baseClient.canBulkGet).toHaveBeenCalledWith(objects, {
|
||||
foo: 'bar',
|
||||
namespace: currentSpace.expectedNamespace,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -332,11 +357,9 @@ const createMockClient = () => {
|
|||
});
|
||||
|
||||
describe('#canFind', () => {
|
||||
test('throws error if types contains space', async () => {
|
||||
test(`throws error if options.namespace is specified`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
baseClient.canFind.mockReturnValue(expectedReturnValue);
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
|
@ -346,10 +369,10 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
await expect(client.canFind(['space'])).rejects.toThrowErrorMatchingSnapshot();
|
||||
await expect(client.canFind({ namespace: 'bar' })).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('supplements types', async () => {
|
||||
test(`throws error if options.type is space`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
|
@ -363,12 +386,93 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
const reqTypes = Object.freeze(['foo', 'bar']);
|
||||
// @ts-ignore
|
||||
const actualReturnValue = await client.canFind(reqTypes);
|
||||
await expect(client.canFind({ type: 'space' })).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test(`passes options.type to baseClient if valid singular type specified`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
baseClient.canFind.mockReturnValue(expectedReturnValue);
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
const options = Object.freeze({ type: 'foo' });
|
||||
|
||||
const actualReturnValue = await client.canFind(options);
|
||||
|
||||
expect(actualReturnValue).toBe(expectedReturnValue);
|
||||
expect(baseClient.canFind).toHaveBeenCalledWith(reqTypes);
|
||||
expect(baseClient.canFind).toHaveBeenCalledWith({
|
||||
type: ['foo'],
|
||||
namespace: currentSpace.expectedNamespace,
|
||||
});
|
||||
});
|
||||
|
||||
test(`throws error if options.type is array containing space`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
baseClient.canFind.mockReturnValue(expectedReturnValue);
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.canFind({ type: ['space', 'foo'] })
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test(`if options.type isn't provided specifies options.type based on the types excluding the space`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
baseClient.canFind.mockReturnValue(expectedReturnValue);
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.canFind({ type: ['space', 'foo'] })
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test(`supplements options with undefined namespace`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
baseClient.canFind.mockReturnValue(expectedReturnValue);
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
|
||||
const options = Object.freeze({ type: ['foo', 'bar'] });
|
||||
const actualReturnValue = await client.canFind(options);
|
||||
|
||||
expect(actualReturnValue).toBe(expectedReturnValue);
|
||||
expect(baseClient.canFind).toHaveBeenCalledWith({
|
||||
type: ['foo', 'bar'],
|
||||
namespace: currentSpace.expectedNamespace,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -499,7 +603,7 @@ const createMockClient = () => {
|
|||
});
|
||||
|
||||
describe('#canBulkCreate', () => {
|
||||
test('throws error if types contains space', async () => {
|
||||
test(`throws error if options.namespace is specified`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const spacesService = createSpacesService(server);
|
||||
|
@ -511,10 +615,32 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
await expect(client.canBulkCreate(['foo', 'space'])).rejects.toThrowErrorMatchingSnapshot();
|
||||
await expect(
|
||||
client.canBulkCreate([{ id: '', type: 'foo', attributes: {} }], { namespace: 'bar' })
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test('supplements options', async () => {
|
||||
test(`throws error if objects type is space`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const spacesService = createSpacesService(server);
|
||||
|
||||
const client = new SpacesSavedObjectsClient({
|
||||
request,
|
||||
baseClient,
|
||||
spacesService,
|
||||
types,
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.canBulkCreate([
|
||||
{ id: '', type: 'foo', attributes: {} },
|
||||
{ id: '', type: 'space', attributes: {} },
|
||||
])
|
||||
).rejects.toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
test(`supplements options with undefined namespace`, async () => {
|
||||
const request = createMockRequest({ id: currentSpace.id });
|
||||
const baseClient = createMockClient();
|
||||
const expectedReturnValue = Symbol();
|
||||
|
@ -528,12 +654,16 @@ const createMockClient = () => {
|
|||
types,
|
||||
});
|
||||
|
||||
const reqTypes = Object.freeze(['foo']);
|
||||
const objects = [{ type: 'foo' }];
|
||||
const options = Object.freeze({ foo: 'bar' });
|
||||
// @ts-ignore
|
||||
const actualReturnValue = await client.canBulkCreate(reqTypes);
|
||||
const actualReturnValue = await client.canBulkCreate(objects, options);
|
||||
|
||||
expect(actualReturnValue).toBe(expectedReturnValue);
|
||||
expect(baseClient.canBulkCreate).toHaveBeenCalledWith(reqTypes);
|
||||
expect(baseClient.canBulkCreate).toHaveBeenCalledWith(objects, {
|
||||
foo: 'bar',
|
||||
namespace: currentSpace.expectedNamespace,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -117,10 +117,14 @@ export class SpacesSavedObjectsClient implements SavedObjectsClient {
|
|||
});
|
||||
}
|
||||
|
||||
public async canBulkCreate(types: string[]) {
|
||||
throwErrorIfTypesContainsSpace(types);
|
||||
public async canBulkCreate(objects: BulkCreateObject[], options: BaseOptions = {}) {
|
||||
throwErrorIfTypesContainsSpace(objects.map(object => object.type));
|
||||
throwErrorIfNamespaceSpecified(options);
|
||||
|
||||
return await this.client.canBulkCreate(types);
|
||||
return await this.client.canBulkCreate(objects, {
|
||||
...options,
|
||||
namespace: getNamespace(this.spaceId),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,10 +178,20 @@ export class SpacesSavedObjectsClient implements SavedObjectsClient {
|
|||
});
|
||||
}
|
||||
|
||||
public async canFind(types: string[]) {
|
||||
throwErrorIfTypesContainsSpace(types);
|
||||
public async canFind(options: FindOptions = {}) {
|
||||
if (options.type) {
|
||||
throwErrorIfTypesContainsSpace(coerceToArray(options.type));
|
||||
}
|
||||
|
||||
return await this.client.canFind(types);
|
||||
throwErrorIfNamespaceSpecified(options);
|
||||
|
||||
return await this.client.canFind({
|
||||
...options,
|
||||
type: (options.type ? coerceToArray(options.type) : this.types).filter(
|
||||
type => type !== 'space'
|
||||
),
|
||||
namespace: getNamespace(this.spaceId),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,10 +218,14 @@ export class SpacesSavedObjectsClient implements SavedObjectsClient {
|
|||
});
|
||||
}
|
||||
|
||||
public async canBulkGet(types: string[]) {
|
||||
throwErrorIfTypesContainsSpace(types);
|
||||
public async canBulkGet(objects: BulkGetObjects = [], options: BaseOptions = {}) {
|
||||
throwErrorIfTypesContainsSpace(objects.map(object => object.type));
|
||||
throwErrorIfNamespaceSpecified(options);
|
||||
|
||||
return await this.client.canBulkGet(types);
|
||||
return await this.client.canBulkGet(objects, {
|
||||
...options,
|
||||
namespace: getNamespace(this.spaceId),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue