UI: Upgrade Ember data 5.3.2 (and upgrade minor versions of ember-source and ember-cli) (#28798)

* upgrade ember-data 5.3.2, uninstall legacy compat, upgrade ember-cli, ember-source

* use query instead of findAll for auth methods, update tests

* set mutableId for kmip

* show generated private key data before transitioning to details

* update kv metadata test

* remove deprecated methods from path help service

* add changelog, update readme version matrix

* remove toggle template helper
This commit is contained in:
claire bontempo
2024-10-30 09:10:22 -07:00
committed by GitHub
parent cc62bf2ac2
commit 17d29f983c
22 changed files with 705 additions and 620 deletions

3
changelog/28798.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:change
ui: Upgrade Ember data to v5.3.2 (and minor upgrade of ember-cli, ember-source to v5.8.0)
```

View File

@@ -24,20 +24,19 @@
This README outlines the details of collaborating on this Ember application. This README outlines the details of collaborating on this Ember application.
## Ember CLI Version Upgrade Matrix ## Ember Version Upgrade Matrix
| Vault Version | Ember Version | Respective versions for `ember-cli`, `ember-source` and `ember-data` for each version of Vault that contains an upgrade.
| ------------- | ------------- |
| 1.17.x | 5.4.2 | | Vault Version | Ember CLI | Ember Source | Ember Data |
| 1.15.x | 4.12.0 | | ------------- | --------- | ------------ | ---------- |
| 1.14.x | 4.4.0 | | 1.19.x | 5.8.0 | 5.8.0 | 5.3.2 |
| 1.13.x | 4.4.0 | | 1.17.x | 5.4.2 | 5.4.0 | 4.12.4 |
| 1.12.x | 3.28.5 | | 1.15.x | 4.12.1 | 4.12.0 | 4.11.3 |
| 1.11.x | 3.28.5 | | 1.13.x | 4.4.0 | 4.4.4 | 4.5.0 |
| 1.10.x | 3.28.5 | | 1.11.x | 3.28.5 | 3.28.10 | 3.28.6 |
| 1.9.x | 3.22.0 | | 1.10.x | 3.24.0 | 3.24.7 | 3.24.0 |
| 1.8.x | 3.22.0 | | 1.9.x | 3.22.0 | 3.22.0 | 3.22.0 |
| 1.7.x | 3.11.0 |
## Prerequisites ## Prerequisites

View File

@@ -23,26 +23,21 @@ export default ApplicationAdapter.extend({
const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated; const isUnauthenticated = snapshotRecordArray?.adapterOptions?.unauthenticated;
// sys/internal/ui/mounts returns the actual value of the system TTL // sys/internal/ui/mounts returns the actual value of the system TTL
// instead of '0' which just indicates the mount is using system defaults // instead of '0' which just indicates the mount is using system defaults
const useMountsEndpoint = snapshotRecordArray?.adapterOptions?.useMountsEndpoint; if (isUnauthenticated) {
if (isUnauthenticated || useMountsEndpoint) {
const url = `/${this.urlPrefix()}/internal/ui/mounts`; const url = `/${this.urlPrefix()}/internal/ui/mounts`;
return this.ajax(url, 'GET', { return this.ajax(url, 'GET', {
unauthenticated: isUnauthenticated, unauthenticated: true,
}) })
.then((result) => { .then((result) => {
return { return {
data: result.data.auth, data: result.data.auth,
}; };
}) })
.catch((e) => { .catch(() => {
if (isUnauthenticated) return { data: {} }; return { data: {} };
if (e instanceof AdapterError) {
set(e, 'policyPath', 'sys/internal/ui/mounts');
}
throw e;
}); });
} }
// if authenticated, findAll will use GET sys/auth instead
return this.ajax(this.url(), 'GET').catch((e) => { return this.ajax(this.url(), 'GET').catch((e) => {
if (e instanceof AdapterError) { if (e instanceof AdapterError) {
set(e, 'policyPath', 'sys/auth'); set(e, 'policyPath', 'sys/auth');
@@ -51,6 +46,25 @@ export default ApplicationAdapter.extend({
}); });
}, },
// findAll makes a network request and supplements the ember-data store with what the API returns.
// after upgrading to ember-data 5.3.2 the store was becoming cluttered with outdated records, so
// use query to refresh the store with each request. this is ideal for list views
query() {
const url = `/${this.urlPrefix()}/internal/ui/mounts`;
return this.ajax(url, 'GET')
.then((result) => {
return {
data: result.data.auth,
};
})
.catch((e) => {
if (e instanceof AdapterError) {
set(e, 'policyPath', 'sys/internal/ui/mounts');
}
throw e;
});
},
createRecord(store, type, snapshot) { createRecord(store, type, snapshot) {
const serializer = store.serializerFor(type.modelName); const serializer = store.serializerFor(type.modelName);
const data = serializer.serialize(snapshot); const data = serializer.serialize(snapshot);

View File

@@ -16,9 +16,18 @@ export default BaseAdapter.extend({
return this._url(...arguments); return this._url(...arguments);
}, },
urlForCreateRecord(modelName, snapshot) { urlForCreateRecord(modelName, snapshot) {
return this._url(snapshot.id, modelName, snapshot); const id = snapshot.record.mutableId;
return this._url(id, modelName, snapshot);
}, },
urlForUpdateRecord() { urlForUpdateRecord() {
return this._url(...arguments); return this._url(...arguments);
}, },
createRecord(store, type, snapshot) {
return this._super(...arguments).then(() => {
// saving returns a 204, return object with id to please ember-data...
const id = snapshot.record.mutableId;
return { id };
});
},
}); });

View File

@@ -64,6 +64,9 @@ export default Component.extend({
), ),
actions: { actions: {
handleToggle(e) {
set(this.key, 'enterAsText', e.target.checked);
},
pickedFile(e) { pickedFile(e) {
const { files } = e.target; const { files } = e.target;
if (!files.length) { if (!files.length) {

View File

@@ -15,9 +15,7 @@ export default Route.extend({
model(params) { model(params) {
const { path } = params; const { path } = params;
return this.store return this.store.query('auth-method', {}).then((modelArray) => {
.findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } })
.then((modelArray) => {
const model = modelArray.find((m) => m.id === path); const model = modelArray.find((m) => m.id === path);
if (!model) { if (!model) {
const error = new AdapterError(); const error = new AdapterError();

View File

@@ -19,6 +19,6 @@ export default class VaultClusterAccessMethodsRoute extends Route {
}; };
model() { model() {
return this.store.findAll('auth-method'); return this.store.query('auth-method', {});
} }
} }

View File

@@ -86,7 +86,7 @@ export default Route.extend(UnloadModelRoute, {
// if you haven't saved a config, the API 404s, so create one here to edit and return it // if you haven't saved a config, the API 404s, so create one here to edit and return it
if (e.httpStatus === 404) { if (e.httpStatus === 404) {
config = this.store.createRecord(modelType, { config = this.store.createRecord(modelType, {
id: backend.id, mutableId: backend.id,
}); });
config.set('backend', backend); config.set('backend', backend);

View File

@@ -49,13 +49,6 @@ export default class PathHelpService extends Service {
// bust cache in EmberData's model lookup // bust cache in EmberData's model lookup
delete store._modelFactoryCache[modelType]; delete store._modelFactoryCache[modelType];
// bust cache in schema service
const schemas = store.getSchemaDefinitionService?.();
if (schemas) {
delete schemas._relationshipsDefCache[modelType];
delete schemas._attributesDefCache[modelType];
}
} }
/** /**

View File

@@ -23,7 +23,7 @@
name={{concat "useText-" this.elementId}} name={{concat "useText-" this.elementId}}
class="toggle is-success is-small" class="toggle is-success is-small"
checked={{this.key.enterAsText}} checked={{this.key.enterAsText}}
onchange={{action (toggle "enterAsText" this.key)}} onchange={{action "handleToggle"}}
/> />
<label for={{concat "useText-" this.elementId}} class="has-text-weight-bold is-size-8"> <label for={{concat "useText-" this.elementId}} class="has-text-weight-bold is-size-8">
Enter as text Enter as text

View File

@@ -17,7 +17,7 @@ export default Route.extend({
return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => { return this.store.findRecord('kmip/config', this.secretMountPath.currentPath).catch((err) => {
if (err.httpStatus === 404) { if (err.httpStatus === 404) {
const model = this.store.createRecord('kmip/config'); const model = this.store.createRecord('kmip/config');
model.set('id', this.secretMountPath.currentPath); model.set('mutableId', this.secretMountPath.currentPath);
return model; return model;
} else { } else {
throw err; throw err;

View File

@@ -3,6 +3,14 @@
SPDX-License-Identifier: BUSL-1.1 SPDX-License-Identifier: BUSL-1.1
~}} ~}}
{{! private_key is only available after initial save }}
{{#if this.generatedKey.privateKey}}
<Page::PkiKeyDetails
@key={{this.generatedKey}}
@canDelete={{this.generatedKey.canDelete}}
@canEdit={{this.generatedKey.canEdit}}
/>
{{else}}
<form {{on "submit" (perform this.save)}}> <form {{on "submit" (perform this.save)}}>
<div class="box is-sideless is-fullwidth is-marginless"> <div class="box is-sideless is-fullwidth is-marginless">
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" /> <MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
@@ -62,3 +70,4 @@
</div> </div>
{{/if}} {{/if}}
</form> </form>
{{/if}}

View File

@@ -39,6 +39,8 @@ export default class PkiKeyForm extends Component<Args> {
@tracked invalidFormAlert = ''; @tracked invalidFormAlert = '';
@tracked modelValidations: ValidationMap | null = null; @tracked modelValidations: ValidationMap | null = null;
@tracked generatedKey: PkiKeyModel | null = null;
@task @task
@waitFor @waitFor
*save(event: Event) { *save(event: Event) {
@@ -51,11 +53,15 @@ export default class PkiKeyForm extends Component<Args> {
this.invalidFormAlert = invalidFormMessage; this.invalidFormAlert = invalidFormMessage;
} }
if (!isValid && isNew) return; if (!isValid && isNew) return;
yield this.args.model.save({ adapterOptions: { import: false } }); this.generatedKey = yield this.args.model.save({ adapterOptions: { import: false } });
this.flashMessages.success( this.flashMessages.success(
`Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}` `Successfully ${isNew ? 'generated' : 'updated'} key${keyName ? ` ${keyName}.` : '.'}`
); );
// only transition to details if there is no private_key data to display
if (!this.generatedKey?.privateKey) {
this.args.onSave(); this.args.onSave();
}
} catch (error) { } catch (error) {
this.errorBanner = errorMessage(error); this.errorBanner = errorMessage(error);
this.invalidFormAlert = 'There was an error submitting this form.'; this.invalidFormAlert = 'There was an error submitting this form.';

View File

@@ -36,4 +36,10 @@ export default class PkiRolesCreateRoute extends Route {
{ label: 'create' }, { label: 'create' },
]; ];
} }
willTransition() {
// after upgrading to Ember Data 5.3.2 we saw duplicate records in the store after creating and saving a new role
// it's unclear why this ghost record is persisting, manually unloading refreshes the store
this.store.unloadAll('pki/role');
}
} }

View File

@@ -66,7 +66,6 @@
"@babel/preset-env": "^7.24.6", "@babel/preset-env": "^7.24.6",
"@babel/preset-typescript": "^7.24.6", "@babel/preset-typescript": "^7.24.6",
"@docfy/ember": "^0.8.5", "@docfy/ember": "^0.8.5",
"@ember-data/legacy-compat": "~4.12.4",
"@ember/legacy-built-in-components": "^0.4.1", "@ember/legacy-built-in-components": "^0.4.1",
"@ember/optional-features": "^2.0.0", "@ember/optional-features": "^2.0.0",
"@ember/render-modifiers": "^1.0.2", "@ember/render-modifiers": "^1.0.2",
@@ -106,7 +105,7 @@
"dompurify": "^3.0.2", "dompurify": "^3.0.2",
"ember-a11y-testing": "^7.0.1", "ember-a11y-testing": "^7.0.1",
"ember-basic-dropdown": "^8.0.4", "ember-basic-dropdown": "^8.0.4",
"ember-cli": "~5.4.2", "ember-cli": "~5.8.0",
"ember-cli-babel": "^8.2.0", "ember-cli-babel": "^8.2.0",
"ember-cli-clean-css": "^3.0.0", "ember-cli-clean-css": "^3.0.0",
"ember-cli-content-security-policy": "2.0.3", "ember-cli-content-security-policy": "2.0.3",
@@ -123,7 +122,7 @@
"ember-cli-terser": "^4.0.2", "ember-cli-terser": "^4.0.2",
"ember-composable-helpers": "5.0.0", "ember-composable-helpers": "5.0.0",
"ember-concurrency": "^4.0.2", "ember-concurrency": "^4.0.2",
"ember-data": "~4.12.4", "ember-data": "~5.3.2",
"ember-engines": "0.8.23", "ember-engines": "0.8.23",
"ember-exam": "^9.0.0", "ember-exam": "^9.0.0",
"ember-inflector": "4.0.2", "ember-inflector": "4.0.2",
@@ -138,7 +137,7 @@
"ember-responsive": "5.0.0", "ember-responsive": "5.0.0",
"ember-service-worker": "meirish/ember-service-worker#configurable-scope", "ember-service-worker": "meirish/ember-service-worker#configurable-scope",
"ember-sinon-qunit": "^7.4.0", "ember-sinon-qunit": "^7.4.0",
"ember-source": "~5.4.0", "ember-source": "~5.8.0",
"ember-style-modifier": "^4.1.0", "ember-style-modifier": "^4.1.0",
"ember-svg-jar": "2.4.4", "ember-svg-jar": "2.4.4",
"ember-template-lint": "^6.0.0", "ember-template-lint": "^6.0.0",

View File

@@ -3,22 +3,17 @@
* SPDX-License-Identifier: BUSL-1.1 * SPDX-License-Identifier: BUSL-1.1
*/ */
import { currentRouteName, click } from '@ember/test-helpers'; import { currentRouteName, click, find, findAll, visit } from '@ember/test-helpers';
import { clickTrigger } from 'ember-power-select/test-support/helpers'; import { clickTrigger } from 'ember-power-select/test-support/helpers';
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit'; import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support'; import { setupMirage } from 'ember-cli-mirage/test-support';
import { create } from 'ember-cli-page-object';
import page from 'vault/tests/pages/access/methods';
import authEnable from 'vault/tests/pages/settings/auth/enable';
import authPage from 'vault/tests/pages/auth';
import ss from 'vault/tests/pages/components/search-select';
import consoleClass from 'vault/tests/pages/components/console/ui-panel';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { mountAuthCmd, runCmd } from 'vault/tests/helpers/commands';
import { login } from 'vault/tests/helpers/auth/auth-helpers';
const consoleComponent = create(consoleClass); const { searchSelect } = GENERAL;
const searchSelect = create(ss);
module('Acceptance | auth-methods list view', function (hooks) { module('Acceptance | auth-methods list view', function (hooks) {
setupApplicationTest(hooks); setupApplicationTest(hooks);
@@ -26,14 +21,13 @@ module('Acceptance | auth-methods list view', function (hooks) {
hooks.beforeEach(function () { hooks.beforeEach(function () {
this.uid = uuidv4(); this.uid = uuidv4();
return authPage.login(); return login();
}); });
test('it navigates to auth method', async function (assert) { test('it navigates to auth method', async function (assert) {
await page.visit(); await visit('/vault/access/');
assert.strictEqual(currentRouteName(), 'vault.cluster.access.methods', 'navigates to the correct route'); assert.strictEqual(currentRouteName(), 'vault.cluster.access.methods', 'navigates to the correct route');
assert.ok(page.methodsLink.isActive, 'the first link is active'); assert.dom('[data-test-sidebar-nav-link="Authentication Methods"]').hasClass('active');
assert.strictEqual(page.methodsLink.text, 'Authentication Methods');
}); });
test('it filters by name and auth type', async function (assert) { test('it filters by name and auth type', async function (assert) {
@@ -41,50 +35,52 @@ module('Acceptance | auth-methods list view', function (hooks) {
const authPath1 = `userpass-1-${this.uid}`; const authPath1 = `userpass-1-${this.uid}`;
const authPath2 = `userpass-2-${this.uid}`; const authPath2 = `userpass-2-${this.uid}`;
const type = 'userpass'; const type = 'userpass';
await authEnable.visit(); await visit('/vault/settings/auth/enable');
await authEnable.enable(type, authPath1); await runCmd(mountAuthCmd(type, authPath1));
await authEnable.visit(); await visit('/vault/settings/auth/enable');
await authEnable.enable(type, authPath2); await runCmd(mountAuthCmd(type, authPath2));
await page.visit(); await visit('/vault/access/');
// filter by auth type
// filter by auth type
await clickTrigger('#filter-by-auth-type'); await clickTrigger('#filter-by-auth-type');
await searchSelect.options.objectAt(0).click(); await click(searchSelect.option(searchSelect.optionIndex(type)));
const rows = document.querySelectorAll('[data-test-auth-backend-link]'); let rows = findAll('[data-test-auth-backend-link]');
const rowsUserpass = Array.from(rows).filter((row) => row.innerText.includes('userpass')); const rowsUserpass = Array.from(rows).filter((row) => row.innerText.includes('userpass'));
assert.strictEqual(rows.length, rowsUserpass.length, 'all rows returned are userpass'); assert.strictEqual(rows.length, rowsUserpass.length, 'all rows returned are userpass');
// filter by name // filter by name
await clickTrigger('#filter-by-auth-name'); await clickTrigger('#filter-by-auth-name');
const firstItemToSelect = searchSelect.options.objectAt(0).text; await click(searchSelect.option());
await searchSelect.options.objectAt(0).click(); const selectedItem = find(`#filter-by-auth-name ${searchSelect.selectedOption()}`).innerText;
const singleRow = document.querySelectorAll('[data-test-auth-backend-link]'); const singleRow = findAll('[data-test-auth-backend-link]');
assert.strictEqual(singleRow.length, 1, 'returns only one row'); assert.strictEqual(singleRow.length, 1, 'returns only one row');
assert.dom(singleRow[0]).includesText(firstItemToSelect, 'shows the filtered by auth name'); assert.dom(singleRow[0]).includesText(selectedItem, 'shows the filtered by auth name');
// clear filter by engine name // clear filter by name
await searchSelect.deleteButtons.objectAt(1).click(); await click(`#filter-by-auth-name ${searchSelect.removeSelected}`);
const rowsAgain = document.querySelectorAll('[data-test-auth-backend-link]'); rows = findAll('[data-test-auth-backend-link]');
assert.ok(rowsAgain.length > 1, 'filter has been removed'); assert.true(rows.length > 1, 'filter has been removed');
// cleanup // cleanup
await consoleComponent.runCommands([`delete sys/auth/${authPath1}`]); await runCmd(`delete sys/auth/${authPath1}`);
await consoleComponent.runCommands([`delete sys/auth/${authPath2}`]); await runCmd(`delete sys/auth/${authPath2}`);
}); });
test('it should show all methods in list view', async function (assert) { test('it should show all methods in list view', async function (assert) {
this.server.get('/sys/auth', () => ({ this.server.get('/sys/internal/ui/mounts', () => ({
data: { data: {
auth: {
'token/': { accessor: 'auth_token_263b8b4e', type: 'token' }, 'token/': { accessor: 'auth_token_263b8b4e', type: 'token' },
'userpass/': { accessor: 'auth_userpass_87aca1f8', type: 'userpass' }, 'userpass/': { accessor: 'auth_userpass_87aca1f8', type: 'userpass' },
}, },
},
})); }));
await page.visit(); await visit('/vault/access/');
assert.dom('[data-test-auth-backend-link]').exists({ count: 2 }, 'All auth methods appear in list view'); assert.dom('[data-test-auth-backend-link]').exists({ count: 2 }, 'All auth methods appear in list view');
await authEnable.visit(); await visit('/vault/settings/auth/enable');
await click('[data-test-sidebar-nav-link="OIDC Provider"]'); await click('[data-test-sidebar-nav-link="OIDC Provider"]');
await page.visit(); await visit('/vault/access/');
assert assert
.dom('[data-test-auth-backend-link]') .dom('[data-test-auth-backend-link]')
.exists({ count: 2 }, 'All auth methods appear in list view after navigating back'); .exists({ count: 2 }, 'All auth methods appear in list view after navigating back');

View File

@@ -307,11 +307,15 @@ module('Acceptance | pki workflow', function (hooks) {
await visit(`/vault/secrets/${this.mountPath}/pki/keys`); await visit(`/vault/secrets/${this.mountPath}/pki/keys`);
await click(PKI_KEYS.generateKey); await click(PKI_KEYS.generateKey);
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/create`); assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/create`);
await fillIn(GENERAL.inputByAttr('type'), 'exported'); await fillIn(GENERAL.inputByAttr('type'), 'exported'); // exported keys generated private_key data
await fillIn(GENERAL.inputByAttr('keyType'), 'rsa'); await fillIn(GENERAL.inputByAttr('keyType'), 'rsa');
await click(GENERAL.saveButton); await click(GENERAL.saveButton);
keyId = find(GENERAL.infoRowValue('Key ID')).textContent?.trim(); keyId = find(GENERAL.infoRowValue('Key ID')).textContent?.trim();
assert.strictEqual(currentURL(), `/vault/secrets/${this.mountPath}/pki/keys/${keyId}/details`); assert.strictEqual(
currentURL(),
`/vault/secrets/${this.mountPath}/pki/keys/create`,
'it does not transition to details private_key data exists'
);
assert assert
.dom(PKI_KEYS.nextStepsAlert) .dom(PKI_KEYS.nextStepsAlert)

View File

@@ -3,15 +3,14 @@
* SPDX-License-Identifier: BUSL-1.1 * SPDX-License-Identifier: BUSL-1.1
*/ */
import { click, currentRouteName, settled } from '@ember/test-helpers'; import { click, currentRouteName, fillIn, visit } from '@ember/test-helpers';
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit'; import { setupApplicationTest } from 'ember-qunit';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { GENERAL } from 'vault/tests/helpers/general-selectors';
import page from 'vault/tests/pages/settings/auth/enable';
import listPage from 'vault/tests/pages/access/methods';
import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { login } from 'vault/tests/helpers/auth/auth-helpers';
import { deleteAuthCmd, runCmd } from 'vault/tests/helpers/commands';
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
module('Acceptance | settings/auth/enable', function (hooks) { module('Acceptance | settings/auth/enable', function (hooks) {
setupApplicationTest(hooks); setupApplicationTest(hooks);
@@ -25,30 +24,34 @@ module('Acceptance | settings/auth/enable', function (hooks) {
// always force the new mount to the top of the list // always force the new mount to the top of the list
const path = `aaa-approle-${this.uid}`; const path = `aaa-approle-${this.uid}`;
const type = 'approle'; const type = 'approle';
await page.visit(); await visit('/vault/settings/auth/enable');
assert.strictEqual(currentRouteName(), 'vault.cluster.settings.auth.enable'); assert.strictEqual(currentRouteName(), 'vault.cluster.settings.auth.enable');
await page.enable(type, path); await click(SES.mountType(type));
await settled(); await fillIn(GENERAL.inputByAttr('path'), path);
assert.strictEqual( await click(SES.mountSubmit);
page.flash.latestMessage, assert
`Successfully mounted the ${type} auth method at ${path}.`, .dom(GENERAL.latestFlashContent)
'success flash shows' .hasText(`Successfully mounted the ${type} auth method at ${path}.`);
);
assert.strictEqual( assert.strictEqual(
currentRouteName(), currentRouteName(),
'vault.cluster.settings.auth.configure.section', 'vault.cluster.settings.auth.configure.section',
'redirects to the auth config page' 'redirects to the auth config page'
); );
await listPage.visit(); await visit('/vault/access/');
assert.ok(listPage.findLinkById(path), 'mount is present in the list'); assert.dom(`[data-test-auth-backend-link=${path}]`).exists('mount is present in the list');
// cleanup
await runCmd(deleteAuthCmd(path));
}); });
test('it renders default config details', async function (assert) { test('it renders default config details', async function (assert) {
const path = `approle-config-${this.uid}`; const path = `approle-config-${this.uid}`;
const type = 'approle'; const type = 'approle';
await page.visit(); await visit('/vault/settings/auth/enable');
await page.enable(type, path); await click(SES.mountType(type));
await fillIn(GENERAL.inputByAttr('path'), path);
await click(SES.mountSubmit);
// the config details is updated to query mount details from sys/internal/ui/mounts // the config details is updated to query mount details from sys/internal/ui/mounts
// but we still want these forms to continue using sys/auth which returns 0 for default ttl values // but we still want these forms to continue using sys/auth which returns 0 for default ttl values
// check tune form (right after enabling) // check tune form (right after enabling)
@@ -64,5 +67,8 @@ module('Acceptance | settings/auth/enable', function (hooks) {
await click('[data-test-configure-link]'); await click('[data-test-configure-link]');
assert.dom(GENERAL.toggleInput('Default Lease TTL')).isNotChecked('default lease ttl is still unset'); assert.dom(GENERAL.toggleInput('Default Lease TTL')).isNotChecked('default lease ttl is still unset');
assert.dom(GENERAL.toggleInput('Max Lease TTL')).isNotChecked('max lease ttl is still unset'); assert.dom(GENERAL.toggleInput('Max Lease TTL')).isNotChecked('max lease ttl is still unset');
// cleanup
await runCmd(deleteAuthCmd(path));
}); });
}); });

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { create, attribute, visitable, collection, hasClass, text } from 'ember-cli-page-object';
export default create({
visit: visitable('/vault/access/'),
methodsLink: {
isActive: hasClass('active'),
text: text(),
scope: '[data-test-sidebar-nav-link="Authentication Methods"]',
},
backendLinks: collection('[data-test-auth-backend-link]', {
path: text('[data-test-path]'),
id: attribute('data-test-id', '[data-test-path]'),
}),
findLinkById(id) {
return this.backendLinks.filterBy('id', id)[0];
},
});

View File

@@ -54,7 +54,7 @@ module('Unit | Adapter | auth method', function (hooks) {
await this.store.findAll('auth-method', { adapterOptions: { unauthenticated: true } }); await this.store.findAll('auth-method', { adapterOptions: { unauthenticated: true } });
}); });
test('findAll makes request to correct endpoint when useMountsEndpoint is true', async function (assert) { test('query makes request to correct endpoint ', async function (assert) {
assert.expect(1); assert.expect(1);
this.server.get('sys/internal/ui/mounts', () => { this.server.get('sys/internal/ui/mounts', () => {
@@ -62,6 +62,6 @@ module('Unit | Adapter | auth method', function (hooks) {
return this.mockResponse; return this.mockResponse;
}); });
await this.store.findAll('auth-method', { adapterOptions: { useMountsEndpoint: true } }); await this.store.query('auth-method', {});
}); });
}); });

View File

@@ -182,7 +182,7 @@ module('Unit | Adapter | kv/metadata', function (hooks) {
let record = await this.store.peekRecord('kv/metadata', data.id); let record = await this.store.peekRecord('kv/metadata', data.id);
await record.destroyRecord(); await record.destroyRecord();
assert.true(record.isDestroyed, 'record is destroyed'); assert.true(record.isDeleted, 'record is deleted');
record = await this.store.peekRecord('kv/metadata', this.id); record = await this.store.peekRecord('kv/metadata', this.id);
assert.strictEqual(record, null, 'record is no longer in store'); assert.strictEqual(record, null, 'record is no longer in store');
}); });

File diff suppressed because it is too large Load Diff