diff --git a/addons/api/addon/models/account.js b/addons/api/addon/models/account.js index 0d42a601d..4c52347f5 100644 --- a/addons/api/addon/models/account.js +++ b/addons/api/addon/models/account.js @@ -14,14 +14,15 @@ export default class AccountModel extends GeneratedAccountModel { /** * Save account password via the `set-password` method. * See serializer and adapter for more information. + * @param {string} password * @param {object} options * @param {object} options.adapterOptions * @return {Promise} */ - savePassword(options={ adapterOptions: {} }) { + setPassword(password, options={ adapterOptions: {} }) { const defaultAdapterOptions = { method: 'set-password', - serializePassword: true + password }; return this.save({ ...options, diff --git a/addons/api/addon/models/fragment-account-attributes.js b/addons/api/addon/models/fragment-account-attributes.js index ee2281f18..76ee82487 100644 --- a/addons/api/addon/models/fragment-account-attributes.js +++ b/addons/api/addon/models/fragment-account-attributes.js @@ -7,7 +7,4 @@ export default class FragmentAccountAttributesModel extends Fragment { description: 'The account login name' }) login_name; - @attr('string', { - description: 'The account password' - }) password; } diff --git a/addons/api/addon/models/fragment-target-attributes.js b/addons/api/addon/models/fragment-target-attributes.js new file mode 100644 index 000000000..1ab994d9d --- /dev/null +++ b/addons/api/addon/models/fragment-target-attributes.js @@ -0,0 +1,10 @@ +import Fragment from 'ember-data-model-fragments/fragment'; +import { attr } from '@ember-data/model'; + +export default class FragmentTargetAttributesModel extends Fragment { + + @attr('number', { + description: 'The default port a target should use if present.' + }) default_port; + +} diff --git a/addons/api/addon/models/target.js b/addons/api/addon/models/target.js index 03a3ad5d7..57b87ff29 100644 --- a/addons/api/addon/models/target.js +++ b/addons/api/addon/models/target.js @@ -1,11 +1,17 @@ import GeneratedTargetModel from '../generated/models/target'; -import { fragmentArray } from 'ember-data-model-fragments/attributes'; +import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes'; import { computed } from '@ember/object'; export default class TargetModel extends GeneratedTargetModel { // =attributes + /** + * Attributes of this resource, if any, represented as a JSON fragment. + * @type {FragmentTargetAttributesModel} + */ + @fragment('fragment-target-attributes', { defaultValue: {} }) attributes; + /** * @type {[FragmentHostSetModel]} */ diff --git a/addons/api/addon/serializers/account.js b/addons/api/addon/serializers/account.js index 361f835eb..c014c2361 100644 --- a/addons/api/addon/serializers/account.js +++ b/addons/api/addon/serializers/account.js @@ -1,33 +1,38 @@ import ApplicationSerializer from './application'; -import { get } from '@ember/object'; export default class AccountSerializer extends ApplicationSerializer { // =methods /** - * If `adapterOptions.serializePassword` is true, the serialization should - * include **password** and the version. + * If `adapterOptions.method` is `set-password`, the serialization should + * include only **password** and the version. + * If `adapterOptions.password` is set, the serialization should + * include **password** in it's `attributes`. * @override * @param {Snapshot} snapshot * @return {object} */ serialize(snapshot) { - const serializePassword = get(snapshot, 'adapterOptions.serializePassword'); + const password = snapshot?.adapterOptions?.password; let serialized = super.serialize(...arguments); - if (serializePassword) serialized = this.serializeWithPassword(snapshot); + if (password && snapshot?.record?.isNew) serialized.attributes.password = password; + if (snapshot?.adapterOptions?.method === 'set-password') { + serialized = this.serializeForSetPassword(snapshot, password); + } return serialized; } /** - * Returns a payload containing only password. + * Returns a payload containing only new password. * @param {Snapshot} snapshot + * @param {string} password * @return {object} */ - serializeWithPassword(snapshot) { + serializeForSetPassword(snapshot, password) { return { version: snapshot.attr('version'), - password: snapshot.attr('attributes').attr('password'), + password, }; } diff --git a/addons/api/app/models/fragment-target-attributes.js b/addons/api/app/models/fragment-target-attributes.js new file mode 100644 index 000000000..41eb7d88f --- /dev/null +++ b/addons/api/app/models/fragment-target-attributes.js @@ -0,0 +1 @@ +export { default } from 'api/models/fragment-target-attributes'; diff --git a/addons/api/tests/unit/models/account-test.js b/addons/api/tests/unit/models/account-test.js index 4237ae6b6..dcd3fe8bb 100644 --- a/addons/api/tests/unit/models/account-test.js +++ b/addons/api/tests/unit/models/account-test.js @@ -4,21 +4,10 @@ import { setupTest } from 'ember-qunit'; module('Unit | Model | account', 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('account', {}); assert.ok(model); }); - test('it contains attributes', function(assert) { - let store = this.owner.lookup('service:store'); - let model = store.createRecord('account', { - type: 'password', - attributes: { - login_name: 'loginname' - } - }); - assert.equal(model.attributes.login_name, 'loginname'); - }); }); diff --git a/addons/api/tests/unit/serializers/account-test.js b/addons/api/tests/unit/serializers/account-test.js index 77b6d19d3..85f9b655f 100644 --- a/addons/api/tests/unit/serializers/account-test.js +++ b/addons/api/tests/unit/serializers/account-test.js @@ -4,16 +4,72 @@ import { setupTest } from 'ember-qunit'; module('Unit | Serializer | account', function(hooks) { setupTest(hooks); - test('it serializes records', function(assert) { - let store = this.owner.lookup('service:store'); - let record = store.createRecord('account', {}); - - let serializedRecord = record.serialize(); - - assert.ok(serializedRecord); + test('it serializes normally', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('account'); + const record = store.createRecord('account', { + type: 'password', + name: 'Name', + auth_method_id: '1', + description: 'Description', + attributes: { + login_name: 'Login Name', + }, + version: 1 + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = {}; + const serializedRecord = serializer.serialize(snapshot); + assert.deepEqual(serializedRecord, { + type: 'password', + name: 'Name', + auth_method_id: '1', + description: 'Description', + attributes: { + login_name: 'Login Name' + }, + version: 1 + }); }); - test('it serializes only password when `adapterOptions.serializePassword` is true', function (assert) { + test('it serializes password attribute when `adapterOptions.password` is true', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('account'); + const record = store.createRecord('account', { + type: 'password', + name: 'Name', + auth_method_id: '1', + description: 'Description', + attributes: { + login_name: 'Login Name', + }, + version: 1 + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + password: 'Password' + }; + const serializedRecord = serializer.serialize(snapshot); + assert.deepEqual(serializedRecord, { + type: 'password', + name: 'Name', + auth_method_id: '1', + description: 'Description', + attributes: { + login_name: 'Login Name', + password: 'Password' + }, + version: 1 + }); + }); + + test('it does not serialize password for non-new records', function (assert) { + assert.expect(0); + }); + + test('it serializes only password when `adapterOptions.method` is set to `set-password`', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('account'); @@ -22,14 +78,15 @@ module('Unit | Serializer | account', function(hooks) { name: 'Name', description: 'Description', attributes: { - login_name: 'Login Name', - password: 'Password' + login_name: 'Login Name' }, + auth_method_id: '1', version: 1 }); const snapshot = record._createSnapshot(); snapshot.adapterOptions = { - serializePassword: true + method: 'set-password', + password: 'Password' }; const serializedRecord = serializer.serialize(snapshot); assert.deepEqual(serializedRecord, { diff --git a/addons/api/tests/unit/serializers/target-test.js b/addons/api/tests/unit/serializers/target-test.js index 94cab05e5..e129331eb 100644 --- a/addons/api/tests/unit/serializers/target-test.js +++ b/addons/api/tests/unit/serializers/target-test.js @@ -30,7 +30,10 @@ module('Unit | Serializer | target', function(hooks) { description: 'Description', version: 1, type: 'tcp', - scope_id: 'org_1' + scope_id: 'org_1', + attributes: { + default_port: null + } }); }); diff --git a/ui/core/app/components/form/target/index.hbs b/ui/core/app/components/form/target/index.hbs index a6e5a204e..5a977d2e4 100644 --- a/ui/core/app/components/form/target/index.hbs +++ b/ui/core/app/components/form/target/index.hbs @@ -14,11 +14,10 @@ {{#if @model.errors.name}} @@ -31,12 +30,10 @@ {{#if @model.errors.description}} @@ -47,6 +44,26 @@ {{/if}} + {{!-- TODO this is a type-specific field --}} + + {{!-- TODO need to figure out how to reference `attributes.errors` here --}} + {{#if @model.errors.default_port}} + + {{#each @model.errors.default_port as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + {{t "resources.auth-method"}} +{{!-- {{t (concat 'auth.method-types.' @model.type)}} - \ No newline at end of file + +--}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs index 6b68c78f7..8586ac7c0 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs @@ -30,9 +30,9 @@ {{else}} - + - {{t "descriptions.auth-method-accounts-welcome"}} + {{t "descriptions.accounts-welcome"}} diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index f3a7cda5f..7f2ea5ba0 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -64,7 +64,7 @@ export default function() { const account = accounts.find(id); const updatedAttrs = { version: attrs.version, - attributes: {} + attributes: account.attributes }; // Set new password if (method === 'set-password') { diff --git a/ui/core/mirage/factories/target.js b/ui/core/mirage/factories/target.js index a96e15556..fc090fde4 100644 --- a/ui/core/mirage/factories/target.js +++ b/ui/core/mirage/factories/target.js @@ -1,11 +1,25 @@ import factory from '../generated/factories/target'; import { trait } from 'ember-cli-mirage'; +import { random } from 'faker'; const randomBoolean = (chance = 0.5) => Math.random() < chance; const hostSetChance = 0.3; export default factory.extend({ + /** + * Generates attributes fields by type. + */ + afterCreate(target) { + if (target.type === 'tcp') { + target.update({ + attributes: { + default_port: random.number() + } + }); + } + }, + /** * Randomly selects existing host sets to assign to target. */ diff --git a/ui/core/mirage/models/auth-method.js b/ui/core/mirage/models/auth-method.js index 878b8bd8a..08b1f5ad3 100644 --- a/ui/core/mirage/models/auth-method.js +++ b/ui/core/mirage/models/auth-method.js @@ -1,8 +1,7 @@ -import { Model, belongsTo, hasMany} from 'ember-cli-mirage'; +import { Model, belongsTo } from 'ember-cli-mirage'; export default Model.extend({ - scope: belongsTo(), - accounts: hasMany('account') + scope: belongsTo() }); diff --git a/ui/core/mirage/serializers/account.js b/ui/core/mirage/serializers/account.js index 7c6186756..85d38851b 100644 --- a/ui/core/mirage/serializers/account.js +++ b/ui/core/mirage/serializers/account.js @@ -6,6 +6,7 @@ export default ApplicationSerializer.extend({ _hashForModel(model) { const json = ApplicationSerializer.prototype._hashForModel.apply(this, arguments); json.auth_method_id = model.authMethodId; + delete json.attributes?.password; return json; } }); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index ed7bbf422..d7e9a610b 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -111,7 +111,7 @@ titles: no-available-users: No Users Available accounts: Accounts account: Account - auth-method-accounts-welcome: Welcome to Accounts + accounts-welcome: Welcome to Accounts descriptions: host-catalogs: Overview of collections of hosts and host sets. empty-set: There are no items to display yet. You may be able to add items or try back later. @@ -132,8 +132,8 @@ descriptions: no-principals-available: Principals are not available for assignment. group-members-welcome: Users belonging to this group appear here. no-available-users: No users are available to add to this group. - auth-method-accounts-welcome: No accounts are available in this auth method. account-settings-overview: Set the password for current account. + accounts-welcome: No accounts are available in this auth method. states: info: Info success: Success @@ -152,6 +152,7 @@ form: address: Address protocol: Protocol actions: Actions + default_port: Default Port errors: titles: unauthenticated-error: You are not signed in