feat: 🎸 add support for role/principals custom methods

This commit is contained in:
Randall Morey committed 2020-08-28 16:26:02 -04:00
1 parent b744f74156
commit 42d21c3a73
3 files changed
+178

No files matched your search

+60
View File
@@ -36,4 +36,64 @@ export default class RoleModel extends GeneratedRoleModel {
});
}
/**
* Adds principals via the `add-principals` method.
* See serializer and adapter for more information.
* @param {[string]} principalIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
addPrincipals(principalIDs, options={ adapterOptions: {} }) {
const defaultAdapterOptions = {
method: 'add-principals',
principalIDs
};
// There is no "deep merge" in ES.
// All of this nonsense is here to ensure we get
// a decent merge of `adapterOptions`.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions
}
});
}
/**
* Delete principals via the `remove-principals` method.
* See serializer and adapter for more information.
* @param {[string]} principalIDs
* @param {object} options
* @param {object} options.adapterOptions
* @return {Promise}
*/
removePrincipals(principalIDs, options={ adapterOptions: {} }) {
const defaultAdapterOptions = {
method: 'remove-principals',
principalIDs
};
// There is no "deep merge" in ES.
// All of this nonsense is here to ensure we get
// a decent merge of `adapterOptions`.
return this.save({
...options,
adapterOptions: {
...defaultAdapterOptions,
...options.adapterOptions
}
});
}
/**
* Delete a single principal via the `remove-principals` method.
* @param {number} principalIDs
* @param {object} options
* @return {Promise}
*/
removePrincipal(principalID, options) {
return this.removePrincipals([principalID], options);
}
}
+19
View File
@@ -9,6 +9,8 @@ export default class RoleSerializer extends ApplicationSerializer {
* If `adapterOptions.serializeGrants` is true, the serialization should
* include **only grants** and the version. Normally, grants are not
* serialized.
* If `adapterOptions.principalIDs` is set (to an array of user and
* group IDs), then the payload is serialized via `serializeWithPrincipals`.
* @override
* @param {Snapshot} snapshot
* @return {object}
@@ -17,6 +19,9 @@ export default class RoleSerializer extends ApplicationSerializer {
const serializeGrants = get(snapshot, 'adapterOptions.serializeGrants');
let serialized = super.serialize(...arguments);
if (serializeGrants) serialized = this.serializeWithGrants(snapshot);
const principalIDs = snapshot?.adapterOptions?.principalIDs;
if (principalIDs) serialized =
this.serializeWithPrincipals(snapshot, principalIDs);
return serialized;
}
@@ -32,6 +37,20 @@ export default class RoleSerializer extends ApplicationSerializer {
};
}
/**
* Returns a payload containing only the principal_ids array using IDs
* passed into the function (rather than existing principals on the model).
* @param {Snapshot} snapshot
* @param {[string]} principalIDs
* @return {object}
*/
serializeWithPrincipals(snapshot, principalIDs) {
return {
version: snapshot.attr('version'),
principal_ids: principalIDs
};
}
/**
* Converts string arrays to object arrays for use with `fragment-string`.
* E.g. `"foo"` -> `{value: "foo"}`
+99
View File
@@ -41,4 +41,103 @@ module('Unit | Model | role', function (hooks) {
const model = store.peekRecord('role', '123abc');
await model.saveGrants();
});
test('it has an `addPrincipals` method that targets a specific POST API endpoint and serialization', async function (assert) {
assert.expect(1);
this.server.post('/v1/scopes/o_1/roles/123abc:add-principals', (schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
principal_ids: ['123_abc', 'foobar'],
version: 1
});
});
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'role',
attributes: {
name: 'Role',
description: 'Description',
principals: [
{ id: '1', type: 'user' },
{ id: '3', type: 'group' }
],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope'
}
}
}
});
const model = store.peekRecord('role', '123abc');
await model.addPrincipals(['123_abc', 'foobar']);
});
test('it has a `removePrincipals` method that targets a specific POST API endpoint and serialization', async function (assert) {
assert.expect(1);
this.server.post('/v1/scopes/o_1/roles/123abc:remove-principals', (schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
principal_ids: ['1', '3'],
version: 1
});
});
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'role',
attributes: {
name: 'Role',
description: 'Description',
principals: [
{ id: '1', type: 'user' },
{ id: '3', type: 'group' }
],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope'
}
}
}
});
const model = store.peekRecord('role', '123abc');
await model.removePrincipals(['1', '3']);
});
test('it has a `removePrincipal` method that deletes a single principal using `removePrincipals` method', async function (assert) {
assert.expect(1);
this.server.post('/v1/scopes/o_1/roles/123abc:remove-principals', (schema, request) => {
const body = JSON.parse(request.requestBody);
assert.deepEqual(body, {
principal_ids: ['3'],
version: 1
});
});
const store = this.owner.lookup('service:store');
store.push({
data: {
id: '123abc',
type: 'role',
attributes: {
name: 'Role',
description: 'Description',
principals: [
{ id: '1', type: 'user' },
{ id: '3', type: 'group' }
],
version: 1,
scope: {
scope_id: 'o_1',
type: 'scope'
}
}
}
});
const model = store.peekRecord('role', '123abc');
await model.removePrincipal('3');
});
});