Skip to content

Commit

Permalink
Add unit tests for consuming permissions in repository
Browse files Browse the repository at this point in the history
Signed-off-by: gaobinlong <gbinlong@amazon.com>
  • Loading branch information
gaobinlong authored and SuZhou-Joe committed Oct 16, 2023
1 parent 6bef445 commit b1c9e88
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 21 deletions.
3 changes: 2 additions & 1 deletion src/core/server/saved_objects/serialization/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class SavedObjectsSerializer {
*/
public rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc {
const { _id, _source, _seq_no, _primary_term } = doc;
const { type, namespace, namespaces, originId, workspaces } = _source;
const { type, namespace, namespaces, originId, workspaces, permissions } = _source;

const version =
_seq_no != null || _primary_term != null
Expand All @@ -86,6 +86,7 @@ export class SavedObjectsSerializer {
...(namespace && this.registry.isSingleNamespace(type) && { namespace }),
...(namespaces && this.registry.isMultiNamespace(type) && { namespaces }),
...(originId && { originId }),
...(permissions && { permissions }),
attributes: _source[type],
references: _source.references || [],
...(_source.migrationVersion && { migrationVersion: _source.migrationVersion }),
Expand Down
178 changes: 162 additions & 16 deletions src/core/server/saved_objects/service/lib/repository.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe('SavedObjectsRepository', () => {
});

const getMockGetResponse = (
{ type, id, references, namespace: objectNamespace, originId, workspaces },
{ type, id, references, namespace: objectNamespace, originId, workspaces, permissions },
namespace
) => {
const namespaceId = objectNamespace === 'default' ? undefined : objectNamespace ?? namespace;
Expand All @@ -189,6 +189,7 @@ describe('SavedObjectsRepository', () => {
...(registry.isMultiNamespace(type) && { namespaces: [namespaceId ?? 'default'] }),
workspaces,
...(originId && { originId }),
...(permissions && { permissions }),
type,
[type]: { title: 'Testing' },
references,
Expand Down Expand Up @@ -451,24 +452,35 @@ describe('SavedObjectsRepository', () => {
};
const namespace = 'foo-namespace';
const workspace = 'foo-workspace';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const getMockBulkCreateResponse = (objects, namespace) => {
return {
items: objects.map(({ type, id, originId, attributes, references, migrationVersion }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
_source: {
[type]: attributes,
type,
namespace,
...(originId && { originId }),
references,
...mockTimestampFields,
migrationVersion: migrationVersion || { [type]: '1.1.1' },
items: objects.map(
({ type, id, originId, attributes, references, migrationVersion, permissions }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
_source: {
[type]: attributes,
type,
namespace,
...(originId && { originId }),
...(permissions && { permissions }),
references,
...mockTimestampFields,
migrationVersion: migrationVersion || { [type]: '1.1.1' },
},
...mockVersionProps,
},
...mockVersionProps,
},
})),
})
),
};
};

Expand Down Expand Up @@ -750,6 +762,18 @@ describe('SavedObjectsRepository', () => {
expect.anything()
);
});

it(`accepts permissions property when providing permissions info`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
await bulkCreateSuccess(objects);
const expected = expect.objectContaining({ permissions });
const body = [expect.any(Object), expected, expect.any(Object), expected];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
client.bulk.mockClear();
});
});

describe('errors', () => {
Expand Down Expand Up @@ -1088,6 +1112,17 @@ describe('SavedObjectsRepository', () => {
);
expect(result.saved_objects[1].id).toEqual(obj2.id);
});

it(`includes permissions property if present`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
const result = await bulkCreateSuccess(objects);
expect(result).toEqual({
saved_objects: [
expect.objectContaining({ permissions }),
expect.objectContaining({ permissions }),
],
});
});
});
});

Expand Down Expand Up @@ -1307,6 +1342,22 @@ describe('SavedObjectsRepository', () => {
],
});
});

it(`includes permissions property if present`, async () => {
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};
const obj = { id: 'three', type: MULTI_NAMESPACE_TYPE, permissions: permissions };
const result = await bulkGetSuccess([obj]);
expect(result).toEqual({
saved_objects: [expect.objectContaining({ permissions: permissions })],
});
});
});
});

Expand All @@ -1324,6 +1375,14 @@ describe('SavedObjectsRepository', () => {
const references = [{ name: 'ref_0', type: 'test', id: '1' }];
const originId = 'some-origin-id';
const namespace = 'foo-namespace';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const getMockBulkUpdateResponse = (objects, options, includeOriginId) => ({
items: objects.map(({ type, id }) => ({
Expand Down Expand Up @@ -1584,6 +1643,20 @@ describe('SavedObjectsRepository', () => {
await bulkUpdateSuccess([{ ..._obj2, namespace }]);
expectClientCallArgsAction([_obj2], { method: 'update', getId, overrides }, 2);
});

it(`accepts permissions property when providing permissions info`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
await bulkUpdateSuccess(objects);
const doc = {
doc: expect.objectContaining({ permissions }),
};
const body = [expect.any(Object), doc, expect.any(Object), doc];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
client.bulk.mockClear();
});
});

describe('errors', () => {
Expand Down Expand Up @@ -1776,6 +1849,14 @@ describe('SavedObjectsRepository', () => {
],
});
});

it(`includes permissions property if present`, async () => {
const obj = { type: MULTI_NAMESPACE_TYPE, id: 'three', permissions: permissions };
const result = await bulkUpdateSuccess([obj1, obj], {}, true);
expect(result).toEqual({
saved_objects: [expect.anything(), expect.objectContaining({ permissions })],
});
});
});
});

Expand Down Expand Up @@ -1965,6 +2046,14 @@ describe('SavedObjectsRepository', () => {
id: '123',
},
];
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const createSuccess = async (type, attributes, options) => {
const result = await savedObjectsRepository.create(type, attributes, options);
Expand Down Expand Up @@ -2189,6 +2278,15 @@ describe('SavedObjectsRepository', () => {
body: expect.objectContaining({
workspaces: ['foo'],
}),
})
);
});

it(`accepts permissions property`, async () => {
await createSuccess(type, attributes, { id, permissions });
expect(client.create).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({ permissions }),
}),
expect.anything()
);
Expand Down Expand Up @@ -2288,6 +2386,11 @@ describe('SavedObjectsRepository', () => {
expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc);
});

it(`adds permissions to body when providing permissions info`, async () => {
await createSuccess(type, attributes, { id, permissions });
expectMigrationArgs({ permissions });
});

it(`adds namespace to body when providing namespace for single-namespace type`, async () => {
await createSuccess(type, attributes, { id, namespace });
expectMigrationArgs({ namespace });
Expand Down Expand Up @@ -2334,11 +2437,13 @@ describe('SavedObjectsRepository', () => {
namespace,
references,
originId,
permissions,
});
expect(result).toEqual({
type,
id,
originId,
permissions,
...mockTimestampFields,
version: mockVersion,
attributes,
Expand Down Expand Up @@ -3208,14 +3313,15 @@ describe('SavedObjectsRepository', () => {
const namespace = 'foo-namespace';
const originId = 'some-origin-id';

const getSuccess = async (type, id, options, includeOriginId) => {
const getSuccess = async (type, id, options, includeOriginId, permissions) => {
const response = getMockGetResponse(
{
type,
id,
// "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the
// operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response.
...(includeOriginId && { originId }),
...(permissions && { permissions }),
},
options?.namespace
);
Expand Down Expand Up @@ -3366,6 +3472,21 @@ describe('SavedObjectsRepository', () => {
const result = await getSuccess(type, id, {}, true);
expect(result).toMatchObject({ originId });
});

it(`includes permissions property if present`, async () => {
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};
const result = await getSuccess(type, id, { namespace }, undefined, permissions);
expect(result).toMatchObject({
permissions: permissions,
});
});
});
});

Expand Down Expand Up @@ -3967,6 +4088,14 @@ describe('SavedObjectsRepository', () => {
},
];
const originId = 'some-origin-id';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const updateSuccess = async (type, id, attributes, options, includeOriginId) => {
if (registry.isMultiNamespace(type)) {
Expand Down Expand Up @@ -4143,6 +4272,18 @@ describe('SavedObjectsRepository', () => {
expect.anything()
);
});

it(`accepts permissions when providing permissions info`, async () => {
await updateSuccess(type, id, attributes, { permissions });
const expected = expect.objectContaining({ permissions });
const body = {
doc: expected,
};
expect(client.update).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
});
});

describe('errors', () => {
Expand Down Expand Up @@ -4237,6 +4378,11 @@ describe('SavedObjectsRepository', () => {
const result = await updateSuccess(type, id, attributes, {}, true);
expect(result).toMatchObject({ originId });
});

it(`includes permissions property if present`, async () => {
const result = await updateSuccess(type, id, attributes, { permissions });
expect(result).toMatchObject({ permissions });
});
});
});
});
10 changes: 7 additions & 3 deletions src/core/server/saved_objects/service/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ export class SavedObjectsRepository {
references: object.references || [],
originId: object.originId,
workspaces: savedObjectWorkspaces,
...(object.permissions && { permissions: object.permissions }),
}) as SavedObjectSanitizedDoc
),
};
Expand Down Expand Up @@ -1222,6 +1223,7 @@ export class SavedObjectsRepository {
namespaces,
...(originId && { originId }),
...(workspaces && { workspaces }),
...(permissions && { permissions }),
references,
attributes,
};
Expand Down Expand Up @@ -1422,7 +1424,7 @@ export class SavedObjectsRepository {
};
}

const { attributes, references, version, namespace: objectNamespace } = object;
const { attributes, references, version, namespace: objectNamespace, permissions } = object;

if (objectNamespace === ALL_NAMESPACES_STRING) {
return {
Expand All @@ -1443,6 +1445,7 @@ export class SavedObjectsRepository {
[type]: attributes,
updated_at: time,
...(Array.isArray(references) && { references }),
...(permissions && { permissions }),
};

const requiresNamespacesCheck = this._registry.isMultiNamespace(object.type);
Expand Down Expand Up @@ -1595,7 +1598,7 @@ export class SavedObjectsRepository {
)[0] as any;

// eslint-disable-next-line @typescript-eslint/naming-convention
const { [type]: attributes, references, updated_at } = documentToSave;
const { [type]: attributes, references, updated_at, permissions } = documentToSave;
if (error) {
return {
id,
Expand All @@ -1615,6 +1618,7 @@ export class SavedObjectsRepository {
version: encodeVersion(seqNo, primaryTerm),
attributes,
references,
...(permissions && { permissions }),
};
}),
};
Expand Down Expand Up @@ -1927,7 +1931,7 @@ function getSavedObjectFromSource<T>(
attributes: doc._source[type],
references: doc._source.references || [],
migrationVersion: doc._source.migrationVersion,
permissions,
...(permissions && { permissions }),
};
}

Expand Down
Loading

0 comments on commit b1c9e88

Please sign in to comment.