From db2548b13abde6c4948f0e5a623adccfeb246fd1 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 2 Aug 2024 12:47:12 -0500 Subject: [PATCH 1/8] Replace getNewModel with hydrateModel when model exists --- .../cluster/secrets/backend/credentials.js | 2 +- .../vault/cluster/secrets/backend/list.js | 2 +- .../cluster/secrets/backend/secret-edit.js | 2 +- ui/app/services/path-help.js | 20 ++++++++++++++++++- ui/lib/kmip/addon/routes/configuration.js | 2 +- ui/lib/kmip/addon/routes/configure.js | 2 +- ui/lib/kmip/addon/routes/role.js | 2 +- ui/lib/kmip/addon/routes/role/edit.js | 2 +- ui/lib/kmip/addon/routes/scope/roles.js | 2 +- .../kmip/addon/routes/scope/roles/create.js | 2 +- ui/lib/pki/addon/routes/application.js | 18 ++++++++--------- ui/tests/unit/services/path-helper-test.js | 12 +++++++++++ 12 files changed, 49 insertions(+), 19 deletions(-) diff --git a/ui/app/routes/vault/cluster/secrets/backend/credentials.js b/ui/app/routes/vault/cluster/secrets/backend/credentials.js index 21f0dbff65de..2ae4d76d28dd 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/credentials.js +++ b/ui/app/routes/vault/cluster/secrets/backend/credentials.js @@ -24,7 +24,7 @@ export default Route.extend({ } // hydrate model if backend type is ssh if (backendType === 'ssh') { - this.pathHelp.getNewModel('ssh-otp-credential', backendPath); + this.pathHelp.hydrateModel('ssh-otp-credential', backendPath); } }, diff --git a/ui/app/routes/vault/cluster/secrets/backend/list.js b/ui/app/routes/vault/cluster/secrets/backend/list.js index fe744044c390..f2ee9eb71b1d 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/list.js +++ b/ui/app/routes/vault/cluster/secrets/backend/list.js @@ -101,7 +101,7 @@ export default Route.extend({ return this.router.transitionTo('vault.cluster.secrets.backend.kv.list', backend); } const modelType = this.getModelType(backend, tab); - return this.pathHelp.getNewModel(modelType, backend).then(() => { + return this.pathHelp.hydrateModel(modelType, backend).then(() => { this.store.unloadAll('capabilities'); }); }, diff --git a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js index 0373a25759c0..70358006fb84 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js +++ b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js @@ -111,7 +111,7 @@ export default Route.extend({ if (modelType === 'secret') { return resolve(); } - return this.pathHelp.getNewModel(modelType, backend); + return this.pathHelp.hydrateModel(modelType, backend); }, modelType(backend, secret, options = {}) { diff --git a/ui/app/services/path-help.js b/ui/app/services/path-help.js index 95d3f182c894..66cb4d416936 100644 --- a/ui/app/services/path-help.js +++ b/ui/app/services/path-help.js @@ -11,7 +11,7 @@ import Model from '@ember-data/model'; import Service from '@ember/service'; import { encodePath } from 'vault/utils/path-encoding-helpers'; -import { getOwner } from '@ember/application'; +import { getOwner } from '@ember/owner'; import { expandOpenApiProps, combineAttributes } from 'vault/utils/openapi-to-attrs'; import fieldToAttrs from 'vault/utils/field-to-attrs'; import { resolve, reject } from 'rsvp'; @@ -41,6 +41,24 @@ export default Service.extend({ }); }, + hydrateModel(modelType, backend) { + const owner = getOwner(this); + const modelName = `model:${modelType}`; + const modelFactory = owner.factoryFor(modelName); + const helpUrl = getHelpUrlForModel(modelType, backend); + + if (!modelFactory) { + throw new Error(`modelFactory for ${modelType} not found -- use getNewModel instead.`); + } + + debug(`Model factory found for ${modelType}`); + const newModel = modelFactory.class; + if (newModel.merged || !helpUrl) { + return resolve(); + } + return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); + }, + /** * getNewModel instantiates models which use OpenAPI fully or partially * @param {string} modelType diff --git a/ui/lib/kmip/addon/routes/configuration.js b/ui/lib/kmip/addon/routes/configuration.js index f1c19f075690..36990b98937c 100644 --- a/ui/lib/kmip/addon/routes/configuration.js +++ b/ui/lib/kmip/addon/routes/configuration.js @@ -12,7 +12,7 @@ export default Route.extend(UnloadModel, { secretMountPath: service(), pathHelp: service(), beforeModel() { - return this.pathHelp.getNewModel('kmip/config', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/config', this.secretMountPath.currentPath); }, model() { return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => { diff --git a/ui/lib/kmip/addon/routes/configure.js b/ui/lib/kmip/addon/routes/configure.js index 98c3f8b19a72..ee4f534ed5ae 100644 --- a/ui/lib/kmip/addon/routes/configure.js +++ b/ui/lib/kmip/addon/routes/configure.js @@ -11,7 +11,7 @@ export default Route.extend({ secretMountPath: service(), pathHelp: service(), beforeModel() { - return this.pathHelp.getNewModel('kmip/config', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/config', this.secretMountPath.currentPath); }, model() { return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => { diff --git a/ui/lib/kmip/addon/routes/role.js b/ui/lib/kmip/addon/routes/role.js index a12b480df6dd..d54f78bddad9 100644 --- a/ui/lib/kmip/addon/routes/role.js +++ b/ui/lib/kmip/addon/routes/role.js @@ -12,7 +12,7 @@ export default class KmipRoleRoute extends Route { @service pathHelp; beforeModel() { - return this.pathHelp.getNewModel('kmip/role', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/role', this.secretMountPath.currentPath); } model(params) { diff --git a/ui/lib/kmip/addon/routes/role/edit.js b/ui/lib/kmip/addon/routes/role/edit.js index 832d746a9b80..8483ea6309fe 100644 --- a/ui/lib/kmip/addon/routes/role/edit.js +++ b/ui/lib/kmip/addon/routes/role/edit.js @@ -11,7 +11,7 @@ export default Route.extend({ secretMountPath: service(), pathHelp: service(), beforeModel() { - return this.pathHelp.getNewModel('kmip/role', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/role', this.secretMountPath.currentPath); }, model() { const params = this.paramsFor(this.routeName); diff --git a/ui/lib/kmip/addon/routes/scope/roles.js b/ui/lib/kmip/addon/routes/scope/roles.js index 46dab7e039cb..4976475d97fc 100644 --- a/ui/lib/kmip/addon/routes/scope/roles.js +++ b/ui/lib/kmip/addon/routes/scope/roles.js @@ -15,7 +15,7 @@ export default Route.extend(ListRoute, { return this.paramsFor('scope').scope_name; }, beforeModel() { - return this.pathHelp.getNewModel('kmip/role', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/role', this.secretMountPath.currentPath); }, model(params) { return this.store diff --git a/ui/lib/kmip/addon/routes/scope/roles/create.js b/ui/lib/kmip/addon/routes/scope/roles/create.js index b6e0f5322e49..82984cddcb63 100644 --- a/ui/lib/kmip/addon/routes/scope/roles/create.js +++ b/ui/lib/kmip/addon/routes/scope/roles/create.js @@ -15,7 +15,7 @@ export default Route.extend({ }, beforeModel() { this.store.unloadAll('kmip/role'); - return this.pathHelp.getNewModel('kmip/role', this.secretMountPath.currentPath); + return this.pathHelp.hydrateModel('kmip/role', this.secretMountPath.currentPath); }, model() { const model = this.store.createRecord('kmip/role', { diff --git a/ui/lib/pki/addon/routes/application.js b/ui/lib/pki/addon/routes/application.js index d8ffa1164518..f601eb99904b 100644 --- a/ui/lib/pki/addon/routes/application.js +++ b/ui/lib/pki/addon/routes/application.js @@ -17,15 +17,15 @@ export default class PkiRoute extends Route { // the openAPI attributes to the model prototype const mountPath = this.secretMountPath.currentPath; return hash({ - acme: this.pathHelp.getNewModel('pki/config/acme', mountPath), - certGenerate: this.pathHelp.getNewModel('pki/certificate/generate', mountPath), - certSign: this.pathHelp.getNewModel('pki/certificate/sign', mountPath), - cluster: this.pathHelp.getNewModel('pki/config/cluster', mountPath), - key: this.pathHelp.getNewModel('pki/key', mountPath), - role: this.pathHelp.getNewModel('pki/role', mountPath), - signCsr: this.pathHelp.getNewModel('pki/sign-intermediate', mountPath), - tidy: this.pathHelp.getNewModel('pki/tidy', mountPath), - urls: this.pathHelp.getNewModel('pki/config/urls', mountPath), + acme: this.pathHelp.hydrateModel('pki/config/acme', mountPath), + certGenerate: this.pathHelp.hydrateModel('pki/certificate/generate', mountPath), + certSign: this.pathHelp.hydrateModel('pki/certificate/sign', mountPath), + cluster: this.pathHelp.hydrateModel('pki/config/cluster', mountPath), + key: this.pathHelp.hydrateModel('pki/key', mountPath), + role: this.pathHelp.hydrateModel('pki/role', mountPath), + signCsr: this.pathHelp.hydrateModel('pki/sign-intermediate', mountPath), + tidy: this.pathHelp.hydrateModel('pki/tidy', mountPath), + urls: this.pathHelp.hydrateModel('pki/config/urls', mountPath), }); } } diff --git a/ui/tests/unit/services/path-helper-test.js b/ui/tests/unit/services/path-helper-test.js index 5612e9e3a385..4f2e54f70671 100644 --- a/ui/tests/unit/services/path-helper-test.js +++ b/ui/tests/unit/services/path-helper-test.js @@ -75,4 +75,16 @@ module('Unit | Service | path-help', function (hooks) { await model.save(); assert.strictEqual(model.get('id'), 'test', 'model id is set to mutableId value on save success'); }); + + test('it should hydrate an existing model', async function (assert) { + assert.expect(2); + + this.server.get(`/v1/pki2/roles/example?help=1`, () => openapiStub); + + const modelType = 'pki/role'; + await this.pathHelp.getNewModel(modelType, 'pki2', 'auth/userpass/', 'user'); + const model = this.store.createRecord(modelType); + model.set('username', 'foobar'); + assert.strictEqual(model.username, 'foobar'); + }); }); From ad8a9eab538c497353e86dc31607d39cefde38f2 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Mon, 5 Aug 2024 15:03:15 -0500 Subject: [PATCH 2/8] Update getNewModel to only handle nonexistant model types --- ui/app/services/path-help.js | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/ui/app/services/path-help.js b/ui/app/services/path-help.js index 66cb4d416936..167a6bb440a4 100644 --- a/ui/app/services/path-help.js +++ b/ui/app/services/path-help.js @@ -44,6 +44,7 @@ export default Service.extend({ hydrateModel(modelType, backend) { const owner = getOwner(this); const modelName = `model:${modelType}`; + const modelFactory = owner.factoryFor(modelName); const helpUrl = getHelpUrlForModel(modelType, backend); @@ -71,29 +72,13 @@ export default Service.extend({ const owner = getOwner(this); const modelName = `model:${modelType}`; - const modelFactory = owner.factoryFor(modelName); - let helpUrl = getHelpUrlForModel(modelType, backend); - - let newModel; - // if we have a factory, we need to take the existing model into account - if (modelFactory) { - debug(`Model factory found for ${modelType}`); - newModel = modelFactory.class; - if (newModel.merged || !helpUrl) { - return resolve(); - } - - return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); - } else { - debug(`Creating new Model for ${modelType}`); - newModel = Model.extend({}); + // if there's an existing factory, throw an error + if (owner.factoryFor(modelName)) { + throw new Error(`Model factory found for ${modelType} - use hydrateModel instead`); } - // we don't have an apiPath for dynamic secrets - // and we don't need paths for them yet - if (!apiPath) { - return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); - } + debug(`Creating new Model for ${modelType}`); + let newModel = Model.extend({}); // use paths to dynamically create our openapi help url // if we have a brand new model @@ -116,7 +101,7 @@ export default Service.extend({ return reject(); } - helpUrl = `/v1/${apiPath}${path.slice(1)}?help=true`; + const helpUrl = `/v1/${apiPath}${path.slice(1)}?help=true`; pathInfo.paths = paths; newModel = newModel.extend({ paths: pathInfo }); return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName); From b5824789026e3a9b358b4e6a69c382a5b1746d27 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Mon, 5 Aug 2024 15:12:28 -0500 Subject: [PATCH 3/8] Update test --- ui/tests/unit/services/path-helper-test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/tests/unit/services/path-helper-test.js b/ui/tests/unit/services/path-helper-test.js index 4f2e54f70671..9bc8856803a7 100644 --- a/ui/tests/unit/services/path-helper-test.js +++ b/ui/tests/unit/services/path-helper-test.js @@ -77,12 +77,10 @@ module('Unit | Service | path-help', function (hooks) { }); test('it should hydrate an existing model', async function (assert) { - assert.expect(2); - - this.server.get(`/v1/pki2/roles/example?help=1`, () => openapiStub); + this.server.get(`/pki2/roles/example`, () => openapiStub); const modelType = 'pki/role'; - await this.pathHelp.getNewModel(modelType, 'pki2', 'auth/userpass/', 'user'); + await this.pathHelp.hydrateModel(modelType, 'pki2'); const model = this.store.createRecord(modelType); model.set('username', 'foobar'); assert.strictEqual(model.username, 'foobar'); From 7b5455d5da761f69cfade7b0eeaf10a5a3a1b41a Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Mon, 5 Aug 2024 15:13:21 -0500 Subject: [PATCH 4/8] clarify test --- ui/tests/unit/services/path-helper-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/tests/unit/services/path-helper-test.js b/ui/tests/unit/services/path-helper-test.js index 9bc8856803a7..98717ffeee04 100644 --- a/ui/tests/unit/services/path-helper-test.js +++ b/ui/tests/unit/services/path-helper-test.js @@ -83,6 +83,6 @@ module('Unit | Service | path-help', function (hooks) { await this.pathHelp.hydrateModel(modelType, 'pki2'); const model = this.store.createRecord(modelType); model.set('username', 'foobar'); - assert.strictEqual(model.username, 'foobar'); + assert.strictEqual(model.username, 'foobar', 'sets value of key that only exists in openAPI response'); }); }); From fbe68dbeee12e3cf0db4827518ee240c90cfae28 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 6 Aug 2024 12:09:58 -0500 Subject: [PATCH 5/8] Fix auth-config models which need hydration not generation --- .../settings/auth/configure/section.js | 5 +++ ui/app/services/path-help.js | 33 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/ui/app/routes/vault/cluster/settings/auth/configure/section.js b/ui/app/routes/vault/cluster/settings/auth/configure/section.js index a17da81fb293..cefaca1282f7 100644 --- a/ui/app/routes/vault/cluster/settings/auth/configure/section.js +++ b/ui/app/routes/vault/cluster/settings/auth/configure/section.js @@ -9,6 +9,7 @@ import { set } from '@ember/object'; import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import UnloadModelRoute from 'vault/mixins/unload-model-route'; +import { getHelpUrlForModel } from 'vault/utils/openapi-helpers'; export default Route.extend(UnloadModelRoute, { modelPath: 'model.model', @@ -41,6 +42,10 @@ export default Route.extend(UnloadModelRoute, { const { method } = this.paramsFor('vault.cluster.settings.auth.configure'); const backend = this.modelFor('vault.cluster.settings.auth.configure'); const modelType = this.modelType(backend.type, section_name); + if (getHelpUrlForModel(modelType, 'test')) { + return this.pathHelp.hydrateModel(modelType, method); + } + // if no helpUrl is defined, this is a fully generated model return this.pathHelp.getNewModel(modelType, method, backend.apiPath); }, diff --git a/ui/app/services/path-help.js b/ui/app/services/path-help.js index 167a6bb440a4..cab83846a343 100644 --- a/ui/app/services/path-help.js +++ b/ui/app/services/path-help.js @@ -41,6 +41,12 @@ export default Service.extend({ }); }, + /** + * hydrateModel instantiates models which use OpenAPI partially + * @param {string} modelType path for model, eg pki/role + * @param {string} backend path, which will be used for the generated helpUrl + * @returns void - as side effect, registers model via registerNewModelWithProps + */ hydrateModel(modelType, backend) { const owner = getOwner(this); const modelName = `model:${modelType}`; @@ -61,10 +67,10 @@ export default Service.extend({ }, /** - * getNewModel instantiates models which use OpenAPI fully or partially + * getNewModel instantiates models which use OpenAPI fully * @param {string} modelType * @param {string} backend - * @param {string} apiPath (optional) if passed, this method will call getPaths and build submodels for item types + * @param {string} apiPath this method will call getPaths and build submodels for item types * @param {*} itemType (optional) used in getPaths for additional models * @returns void - as side effect, registers model via registerNewModelWithProps */ @@ -72,13 +78,22 @@ export default Service.extend({ const owner = getOwner(this); const modelName = `model:${modelType}`; - // if there's an existing factory, throw an error - if (owner.factoryFor(modelName)) { - throw new Error(`Model factory found for ${modelType} - use hydrateModel instead`); - } + const modelFactory = owner.factoryFor(modelName); - debug(`Creating new Model for ${modelType}`); - let newModel = Model.extend({}); + let newModel; + // if we have a factory, we need to take the existing model into account + if (modelFactory) { + debug(`Model factory found for ${modelType}`); + newModel = modelFactory.class; + if (newModel.merged) { + return resolve(); + } + // if we get here, that means an existing non-generated model exists + throw new Error(`Model exists for ${modelType} -- use hydrateModel instead`); + } else { + debug(`Creating new Model for ${modelType}`); + newModel = Model.extend({}); + } // use paths to dynamically create our openapi help url // if we have a brand new model @@ -310,7 +325,7 @@ export default Service.extend({ }, }), }); - newModel.reopenClass({ merged: true }); + newModel.merged = true; owner.unregister(modelName); owner.register(modelName, newModel); }); From e5a6c1c58918b96d5b717c2a8be841c972060131 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 6 Aug 2024 14:03:06 -0500 Subject: [PATCH 6/8] rename file to match service name --- ui/tests/unit/services/{path-helper-test.js => path-help-test.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ui/tests/unit/services/{path-helper-test.js => path-help-test.js} (100%) diff --git a/ui/tests/unit/services/path-helper-test.js b/ui/tests/unit/services/path-help-test.js similarity index 100% rename from ui/tests/unit/services/path-helper-test.js rename to ui/tests/unit/services/path-help-test.js From 2381b2fe89b4166d41feb93587a3f66cfa15e6a6 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 6 Aug 2024 17:54:56 -0500 Subject: [PATCH 7/8] cleanup + tests --- ui/app/services/path-help.js | 22 ++++---- ui/tests/unit/services/path-help-test.js | 70 +++++++++++++++++------- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/ui/app/services/path-help.js b/ui/app/services/path-help.js index cab83846a343..07fc64416b01 100644 --- a/ui/app/services/path-help.js +++ b/ui/app/services/path-help.js @@ -67,7 +67,7 @@ export default Service.extend({ }, /** - * getNewModel instantiates models which use OpenAPI fully + * getNewModel instantiates models which use OpenAPI to generate the model fully * @param {string} modelType * @param {string} backend * @param {string} apiPath this method will call getPaths and build submodels for item types @@ -80,20 +80,18 @@ export default Service.extend({ const modelFactory = owner.factoryFor(modelName); - let newModel; - // if we have a factory, we need to take the existing model into account if (modelFactory) { - debug(`Model factory found for ${modelType}`); - newModel = modelFactory.class; - if (newModel.merged) { - return resolve(); + // if the modelFactory already exists, it means either this model was already + // generated or the model exists in the code already. In either case resolve + + if (!modelFactory.class.merged) { + // no merged flag means this model was not previously generated + debug(`Model exists for ${modelType} -- use hydrateModel instead`); } - // if we get here, that means an existing non-generated model exists - throw new Error(`Model exists for ${modelType} -- use hydrateModel instead`); - } else { - debug(`Creating new Model for ${modelType}`); - newModel = Model.extend({}); + return resolve(); } + debug(`Creating new Model for ${modelType}`); + let newModel = Model.extend({}); // use paths to dynamically create our openapi help url // if we have a brand new model diff --git a/ui/tests/unit/services/path-help-test.js b/ui/tests/unit/services/path-help-test.js index 98717ffeee04..1a5a2b273daa 100644 --- a/ui/tests/unit/services/path-help-test.js +++ b/ui/tests/unit/services/path-help-test.js @@ -6,6 +6,8 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; +import Sinon from 'sinon'; +import { reject } from 'rsvp'; const openapiStub = { openapi: { @@ -58,31 +60,59 @@ module('Unit | Service | path-help', function (hooks) { this.store = this.owner.lookup('service:store'); }); - test('it should generate model with mutableId', async function (assert) { - assert.expect(2); + module('getNewModel', function (hooks) { + hooks.beforeEach(function () { + this.server.get('/auth/userpass/', () => openapiStub); + this.server.get('/auth/userpass/users/example', () => openapiStub); + }); + test('it generates a model with mutableId', async function (assert) { + assert.expect(2); + this.server.post('/auth/userpass/users/test', () => { + assert.true(true, 'POST request made to correct endpoint'); + return; + }); - this.server.get('/auth/userpass/', () => openapiStub); - this.server.get('/auth/userpass/users/example', () => openapiStub); - this.server.post('/auth/userpass/users/test', () => { - assert.ok(true, 'POST request made to correct endpoint'); - return; + const modelType = 'generated-user-userpass'; + await this.pathHelp.getNewModel(modelType, 'userpass', 'auth/userpass/', 'user'); + const model = this.store.createRecord(modelType); + model.set('mutableId', 'test'); + await model.save(); + assert.strictEqual(model.get('id'), 'test', 'model id is set to mutableId value on save success'); }); - const modelType = 'generated-user-userpass'; - await this.pathHelp.getNewModel(modelType, 'userpass', 'auth/userpass/', 'user'); - const model = this.store.createRecord(modelType); - model.set('mutableId', 'test'); - await model.save(); - assert.strictEqual(model.get('id'), 'test', 'model id is set to mutableId value on save success'); + test('it only generates the model once', async function (assert) { + assert.expect(2); + Sinon.spy(this.pathHelp, 'getPaths'); + + const modelType = 'generated-user-userpass'; + await this.pathHelp.getNewModel(modelType, 'userpass', 'auth/userpass/', 'user'); + assert.true(this.pathHelp.getPaths.calledOnce, 'getPaths is called for new generated model'); + + await this.pathHelp.getNewModel(modelType, 'userpass2', 'auth/userpass/', 'user'); + assert.true(this.pathHelp.getPaths.calledOnce, 'not called again even with different backend path'); + }); + + test('it resolves without error if model already exists', async function (assert) { + Sinon.stub(this.pathHelp, 'getPaths').callsFake(() => { + assert.notOk(true, 'this method should not be called'); + return reject(); + }); + const modelType = 'kv/data'; + await this.pathHelp.getNewModel(modelType, 'my-kv').then(() => { + assert.true(true, 'getNewModel resolves'); + }); + }); }); - test('it should hydrate an existing model', async function (assert) { - this.server.get(`/pki2/roles/example`, () => openapiStub); + module('hydrateModel', function () { + test('it should hydrate an existing model', async function (assert) { + this.server.get(`/pki2/roles/example`, () => openapiStub); - const modelType = 'pki/role'; - await this.pathHelp.hydrateModel(modelType, 'pki2'); - const model = this.store.createRecord(modelType); - model.set('username', 'foobar'); - assert.strictEqual(model.username, 'foobar', 'sets value of key that only exists in openAPI response'); + const modelType = 'pki/role'; + await this.pathHelp.hydrateModel(modelType, 'pki2'); + const model = this.store.createRecord(modelType); + model.set('username', 'foobar'); + assert.strictEqual(model.username, 'foobar', 'sets value of key that only exists in openAPI response'); + }); }); }); From 96cb13b257304abd30d443b44b2cc0852f5f48b6 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 7 Aug 2024 10:27:42 -0500 Subject: [PATCH 8/8] Add comment about helpUrl method --- ui/app/routes/vault/cluster/settings/auth/configure/section.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/app/routes/vault/cluster/settings/auth/configure/section.js b/ui/app/routes/vault/cluster/settings/auth/configure/section.js index cefaca1282f7..0111f636ee1c 100644 --- a/ui/app/routes/vault/cluster/settings/auth/configure/section.js +++ b/ui/app/routes/vault/cluster/settings/auth/configure/section.js @@ -42,7 +42,8 @@ export default Route.extend(UnloadModelRoute, { const { method } = this.paramsFor('vault.cluster.settings.auth.configure'); const backend = this.modelFor('vault.cluster.settings.auth.configure'); const modelType = this.modelType(backend.type, section_name); - if (getHelpUrlForModel(modelType, 'test')) { + // If this method returns a string it means we expect to hydrate it with OpenAPI + if (getHelpUrlForModel(modelType)) { return this.pathHelp.hydrateModel(modelType, method); } // if no helpUrl is defined, this is a fully generated model