diff --git a/addons/api/addon/models/fragment-credential-library.js b/addons/api/addon/models/fragment-credential-library.js new file mode 100644 index 000000000..384ffef1e --- /dev/null +++ b/addons/api/addon/models/fragment-credential-library.js @@ -0,0 +1,11 @@ +import Fragment from 'ember-data-model-fragments/fragment'; +import { attr } from '@ember-data/model'; + +export default class FragmentCredentialLibraryModel extends Fragment { + // =attributes + + @attr('string', { + description: 'ID of the related credential library', + }) + credential_library_id; +} diff --git a/addons/api/addon/models/target.js b/addons/api/addon/models/target.js index a00c3b182..3788e595f 100644 --- a/addons/api/addon/models/target.js +++ b/addons/api/addon/models/target.js @@ -20,6 +20,15 @@ export default class TargetModel extends GeneratedTargetModel { }) host_sets; + /** + * @type {[FragmentCredentialLibrary]} + */ + @fragmentArray('fragment-credential-library', { + readOnly: true, + emptyArrayIfMissing: true, + }) + credential_libraries; + /** * An array of resolved host set and host catalog instances. Model instances * must already be loaded into the store (this method will not load unloaded @@ -65,6 +74,19 @@ export default class TargetModel extends GeneratedTargetModel { return Boolean(pendingOrActiveSessions.length); } + /** + * An array of resolved credential library instances. Model instances + * must already be loaded into the store (this method will not load unloaded + * instances). Unresolvable instances are excluded from the array. + * @type {[CredentialLibraryModel]} + */ + @computed('credential_libraries.[]', 'store') + get credentialLibraries() { + return this.credential_libraries + .map((id) => this.store.peekRecord('credential-library', id)) + .filter(Boolean); + } + // =methods /** diff --git a/addons/api/addon/serializers/fragment-credential-library.js b/addons/api/addon/serializers/fragment-credential-library.js new file mode 100644 index 000000000..9f3d01c81 --- /dev/null +++ b/addons/api/addon/serializers/fragment-credential-library.js @@ -0,0 +1,16 @@ +import JSONSerializer from '@ember-data/serializer/json'; + +export default class FragmentCredentialLibrarySerializer extends JSONSerializer { + // =methods + + /** + * Renames `id` to `credential_library_id` because fragments may not have IDs. + * Otherwise normalizes as normal. + * @param {ModelClass} typeClass + * @param {object} resourceHash + * @return {object} + */ + normalize(typeClass, { id: credential_library_id, ...obj }) { + return super.normalize(typeClass, { credential_library_id, ...obj }); + } +} diff --git a/addons/api/addon/serializers/target.js b/addons/api/addon/serializers/target.js index 4c6745b89..c287aca46 100644 --- a/addons/api/addon/serializers/target.js +++ b/addons/api/addon/serializers/target.js @@ -6,7 +6,11 @@ export default class TargetSerializer extends ApplicationSerializer { /** * If `adapterOptions.hostSets` is set to an array of host set models, * the resulting target serialization should include **only host sets** - * and the version. Normally, host sets are not serialized. + * and the version. + * If `adapterOptions.credentialLibraries` is set to an array of + * credential library models, the resulting target serialization should + * include **only credential libraries** and the version. + * Normally, neither host sets or credential libraries are not serialized. * @override * @param {Snapshot} snapshot * @return {object} @@ -16,6 +20,12 @@ export default class TargetSerializer extends ApplicationSerializer { const hostSetIDs = snapshot?.adapterOptions?.hostSetIDs; if (hostSetIDs) serialized = this.serializeWithHostSets(snapshot, hostSetIDs); + const credentialLibraryIDs = snapshot?.adapterOptions?.credentialLibraryIDs; + if (credentialLibraryIDs) + serialized = this.serializeWithCredentialLibraries( + snapshot, + credentialLibraryIDs + ); return serialized; } @@ -32,4 +42,18 @@ export default class TargetSerializer extends ApplicationSerializer { host_set_ids: hostSetIDs, }; } + + /** + * Returns a payload containing only the credential_library_ids array using IDs + * passed into the function (rather than existing credential libraries on the model). + * @param {Snapshot} snapshot + * @param {[string]} credentialLibraryIDs + * @return {object} + */ + serializeWithCredentialLibraries(snapshot, credentialLibraryIDs) { + return { + version: snapshot.attr('version'), + application_credential_library_ids: credentialLibraryIDs, + }; + } } diff --git a/addons/api/app/models/fragment-credential-library.js b/addons/api/app/models/fragment-credential-library.js new file mode 100644 index 000000000..4cee0ae38 --- /dev/null +++ b/addons/api/app/models/fragment-credential-library.js @@ -0,0 +1 @@ +export { default } from 'api/models/fragment-credential-library'; diff --git a/addons/api/app/serializers/fragment-credential-library.js b/addons/api/app/serializers/fragment-credential-library.js new file mode 100644 index 000000000..b60ecfef5 --- /dev/null +++ b/addons/api/app/serializers/fragment-credential-library.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/fragment-credential-library'; diff --git a/addons/api/mirage/factories/target.js b/addons/api/mirage/factories/target.js index dc1c97d00..8ae03edbe 100644 --- a/addons/api/mirage/factories/target.js +++ b/addons/api/mirage/factories/target.js @@ -7,7 +7,7 @@ const hostSetChance = 0.3; export default factory.extend({ /** - * -1 means "unlimited" and we want to generate these on occassion. + * -1 means "unlimited" and we want to generate these on occasion. */ session_connection_limit: () => random.arrayElement([-1, datatype.number()]), @@ -25,11 +25,11 @@ export default factory.extend({ }, /** - * Randomly selects existing host sets to assign to target. + * Randomly selects existing host sets and credential libraries to assign to target. */ - withRandomHostSets: trait({ + withAssociations: trait({ afterCreate(target, server) { - let randomlySelectedHostSets; + let randomlySelectedHostSets, randomlySelectedCredentialLibraries; randomlySelectedHostSets = server.schema.hostSets // BLERG: fun fact, for no reason at all, the element passed // into a where function is not a full model instance, as you might @@ -38,7 +38,15 @@ export default factory.extend({ // the result set of `where` _is a collection of model instances_. .where((hostSet) => hostSet.scopeId === target.scope.id) .models.filter(() => randomBoolean(hostSetChance)); - target.update({ hostSets: randomlySelectedHostSets }); + + randomlySelectedCredentialLibraries = server.schema.credentialLibraries + .where((credentialLibrary) => credentialLibrary.scopeId === target.scope.id) + .models.filter(() => randomBoolean()); + + target.update({ + hostSets: randomlySelectedHostSets, + credentialLibraries: randomlySelectedCredentialLibraries, + }); }, }), }); diff --git a/addons/api/mirage/models/target.js b/addons/api/mirage/models/target.js index 0f03cb6ef..389e255d2 100644 --- a/addons/api/mirage/models/target.js +++ b/addons/api/mirage/models/target.js @@ -3,4 +3,5 @@ import { Model, belongsTo, hasMany } from 'ember-cli-mirage'; export default Model.extend({ scope: belongsTo(), hostSets: hasMany(), + credentialLibraries: hasMany(), }); diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index fc5931908..d448a443b 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -47,8 +47,8 @@ export default function (server) { // Other resources server.schema.scopes.where({ type: 'project' }).models.forEach((scope) => { server.createList('host-catalog', 2, { scope }, 'withChildren'); - server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); server.createList('credential-store', 3, { scope }, 'withAssociations'); + server.createList('target', 2, { scope }, 'withAssociations'); }); } diff --git a/addons/api/mirage/serializers/target.js b/addons/api/mirage/serializers/target.js index 006388773..c23330189 100644 --- a/addons/api/mirage/serializers/target.js +++ b/addons/api/mirage/serializers/target.js @@ -23,6 +23,9 @@ export default ApplicationSerializer.extend({ return { id: host_set_id, host_catalog_id }; }); } + if (model.credentialLibraryIds?.length) { + json.credential_libraries = model.credentialLibraryIds; + } return json; }, }); diff --git a/addons/api/tests/unit/models/fragment-credential-library-test.js b/addons/api/tests/unit/models/fragment-credential-library-test.js new file mode 100644 index 000000000..f5014569e --- /dev/null +++ b/addons/api/tests/unit/models/fragment-credential-library-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | fragment credential library', 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('fragment-credential-library', {}); + assert.ok(model); + }); +}); diff --git a/addons/api/tests/unit/serializers/fragment-credential-library-test.js b/addons/api/tests/unit/serializers/fragment-credential-library-test.js new file mode 100644 index 000000000..719d97955 --- /dev/null +++ b/addons/api/tests/unit/serializers/fragment-credential-library-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | fragment credential library', function (hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function (assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('fragment-credential-library'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('fragment-credential-library', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +});