From 322828ccc450c0a1a494d0bc222f7d158895c741 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Thu, 28 Oct 2021 08:30:28 -0700 Subject: [PATCH] First iteration API modeling managed groups --- .../addon/generated/models/managed-groups.js | 47 ++++++++++ addons/api/addon/models/managed-groups.js | 89 +++++++++++++++++++ .../api/addon/serializers/managed-groups.js | 32 +++++++ addons/api/app/models/managed-groups.js | 1 + .../tests/unit/models/managed-groups-test.js | 13 +++ .../unit/serializers/managed-groups-test.js | 56 ++++++++++++ 6 files changed, 238 insertions(+) create mode 100644 addons/api/addon/generated/models/managed-groups.js create mode 100644 addons/api/addon/models/managed-groups.js create mode 100644 addons/api/addon/serializers/managed-groups.js create mode 100644 addons/api/app/models/managed-groups.js create mode 100644 addons/api/tests/unit/models/managed-groups-test.js create mode 100644 addons/api/tests/unit/serializers/managed-groups-test.js diff --git a/addons/api/addon/generated/models/managed-groups.js b/addons/api/addon/generated/models/managed-groups.js new file mode 100644 index 000000000..d6502eee6 --- /dev/null +++ b/addons/api/addon/generated/models/managed-groups.js @@ -0,0 +1,47 @@ +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * A managed group is a resource that represents a collection of accounts. + */ +export default class GeneratedManagedGroupModel extends BaseModel { + // =attributes + + @attr('string', { + description: 'Optional name for identification purposes.', + }) + name; + + @attr('string', { + description: 'Optional user-set description for identification purposes.', + }) + description; + + @attr('date', { + description: 'The time this resource was created', + readOnly: true, + }) + created_time; + + @attr('date', { + description: 'The time this resource was last updated.', + readOnly: true, + }) + updated_time; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + + @attr('string', { + description: 'The type of this ManagedGroup.', + }) + type; + + @attr('string', { + description: + 'The ID of the Auth Method that is associated with this ManagedGroup', + }) + auth_method_id; +} diff --git a/addons/api/addon/models/managed-groups.js b/addons/api/addon/models/managed-groups.js new file mode 100644 index 000000000..ddc2d2ba4 --- /dev/null +++ b/addons/api/addon/models/managed-groups.js @@ -0,0 +1,89 @@ +import GeneratedManagedGroupModel from '../generated/models/managed-groups'; +import { attr } from '@ember-data/model'; +import { computed } from '@ember/object'; +import { A } from '@ember/array'; + +export default class ManagedGroupsModel extends GeneratedManagedGroupModel { + // =attributes + /** + * Members is read-only under normal circumstances. But members can + * be persisted via calls to `addMembers()` or `removeMembers()`. + */ + @attr({ + readOnly: true, + defaultValue: () => A(), + emptyArrayIfMissing: true, + }) + member_ids; + + /** + * An array of resolved user instances. + * Unresolvable instances are excluded from the array. + * @type {[UserModel]} + */ + // TODO: Use fragments array to read cross scope members. + @computed('member_ids.[]', 'store') + get members() { + return this.member_ids + .map((id) => this.store.peekRecord('user', id)) + .filter(Boolean); + } + + // =methods + + /** + * Adds members via the `add-members` method. + * See serializer and adapter for more information. + * @param {[string]} memberIDs + * @param {object} options + * @param {object} options.adapterOptions + * @return {Promise} + */ + addMembers(memberIDs, options = { adapterOptions: {} }) { + const defaultAdapterOptions = { + method: 'add-members', + memberIDs, + }; + // There is no "deep merge" in ES. + return this.save({ + ...options, + adapterOptions: { + ...defaultAdapterOptions, + ...options.adapterOptions, + }, + }); + } + + /** + * Delete members via the `remove-members` method. + * See serializer and adapter for more information. + * @param {[string]} memberIDs + * @param {object} options + * @param {object} options.adapterOptions + * @return {Promise} + */ + removeMembers(memberIDs, options = { adapterOptions: {} }) { + const defaultAdapterOptions = { + method: 'remove-members', + memberIDs, + }; + // There is no "deep merge" in ES. + return this.save({ + ...options, + adapterOptions: { + ...defaultAdapterOptions, + ...options.adapterOptions, + }, + }); + } + + /** + * Delete a single member via the `remove-members` method. + * @param {number} memberID + * @param {object} options + * @return {Promise} + */ + removeMember(memberID, options) { + return this.removeMembers([memberID], options); + } +} diff --git a/addons/api/addon/serializers/managed-groups.js b/addons/api/addon/serializers/managed-groups.js new file mode 100644 index 000000000..3757bcbe0 --- /dev/null +++ b/addons/api/addon/serializers/managed-groups.js @@ -0,0 +1,32 @@ +import ApplicationSerializer from './application'; + +export default class ManagedGroupsSerializer extends ApplicationSerializer { + // =methods + + /** + * If `adapterOptions.memberIDs` is set (to an array of user IDs), + * then the payload is serialized via `serializewithMembers`. + * @override + * @param {Snapshot} snapshot + * @return {object} + */ + serialize(snapshot) { + let serialized = super.serialize(...arguments); + const memberIDs = snapshot?.adapterOptions?.memberIDs; + if (memberIDs) serialized = this.serializewithMembers(snapshot, memberIDs); + return serialized; + } + + /** + * Returns a payload containing only the `member_ids` array (and version). + * @param {Snapshot} snapshot + * @param {[string]} memberIDs + * @return {object} + */ + serializewithMembers(snapshot, memberIDs) { + return { + version: snapshot.attr('version'), + member_ids: memberIDs, + }; + } +} diff --git a/addons/api/app/models/managed-groups.js b/addons/api/app/models/managed-groups.js new file mode 100644 index 000000000..4fb49c35a --- /dev/null +++ b/addons/api/app/models/managed-groups.js @@ -0,0 +1 @@ +export { default } from 'api/models/managed-groups'; diff --git a/addons/api/tests/unit/models/managed-groups-test.js b/addons/api/tests/unit/models/managed-groups-test.js new file mode 100644 index 000000000..426faa008 --- /dev/null +++ b/addons/api/tests/unit/models/managed-groups-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | managed groups', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('managed-groups', {}); + assert.ok(model); + }); +}); diff --git a/addons/api/tests/unit/serializers/managed-groups-test.js b/addons/api/tests/unit/serializers/managed-groups-test.js new file mode 100644 index 000000000..f4f68de33 --- /dev/null +++ b/addons/api/tests/unit/serializers/managed-groups-test.js @@ -0,0 +1,56 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | Managed groups', function (hooks) { + setupTest(hooks); + + test('it serializes managed groups normally, without members', function (assert) { + assert.expect(1); + // TODO: move store declaration at module level. Before doing so, to make sure it works, generate one out, one in and deep compare. + // TODO: if moving store out works, move serializer too. + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('managed-groups'); + const record = store.createRecord('managed-groups', { + name: 'Group', + description: 'Description', + member_ids: ['1', '2'], + version: 1, + auth_method_id: '1234', + type: '80', + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = {}; + const serializedRecord = serializer.serialize(snapshot); + assert.deepEqual(serializedRecord, { + name: 'Group', + description: 'Description', + version: 1, + auth_method_id: '1234', + type: '80', + }); + }); + + test('it seralizes only members associated with this group and version when a `memberIDs` array is provided', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('managed-groups'); + const record = store.createRecord('managed-groups', { + name: 'Group', + description: 'Description', + member_ids: ['1', '2'], + version: 1, + auth_method_id: '1234', + type: '80', + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + memberIDs: ['4', '5'], + }; + const serializedRecord = serializer.serialize(snapshot); + console.log(JSON.stringify(serializedRecord)); + assert.deepEqual(serializedRecord, { + member_ids: ['4', '5'], + version: 1, + }); + }); +});