refactor: 💡 remove route action usage for scopes (#2385)

* refactor: 💡 remove route action usage for scopes

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13755
This commit is contained in:
Lisbet Alvarez authored and GitHub committed 2024-08-15 10:46:00 -07:00
1 parent 9e668e8467
commit 0af81d9bc1
27 files changed
+755 -260

No files matched your search

@@ -7,6 +7,7 @@
class='full-width'
@cancel={{@cancel}}
@onSubmit={{this.submit}}
@disabled={{this.isSaving}}
as |form|
>
<div class='create-resources-form'>
@@ -9,12 +9,26 @@ import { tracked } from '@glimmer/tracking';
export default class FormOnboardingComponent extends Component {
// =properties
@tracked targetAddress;
@tracked targetAddressInvalid = false;
@tracked targetPort;
@tracked targetPortInvalid = false;
/**
* Returns true if any of the resources that are being created are still saving.
* @type {boolean}
*/
get isSaving() {
const { org, project, target, role } = this.args.model;
return org.isSaving || project.isSaving || target.isSaving || role.isSaving;
}
// =actions
/**
* Passes in target address and port to submit function for further proccessing.
*/
@action
submit() {
// Check targetAddress and targetPort are valid before submit
@@ -0,0 +1,126 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { notifyError } from 'core/decorators/notify';
export default class OnboardingIndexController extends Controller {
// =services
@service intl;
@service router;
// =actions
/**
* Creates resources then redirects to success page.
* @param {object} model
* @param {string} targetAddress
* @param {string} targetPort
*/
@action
@loading
@notifyError(function () {
return this.intl.t('errors.onboarding-failed.description');
})
async createResources(model, targetAddress, targetPort) {
const { org, project, target, role } = model;
await this.createOrg(org);
await this.createProject(org, project);
await this.createTarget(project, target, targetAddress, targetPort);
await this.createGrants(org, project, target, role);
// Redirect to success
this.router.transitionTo('onboarding.success');
}
/**
* Creates Org, if fails, redirects to Orgs empty list
* @param {ScopeModel} org
*/
async createOrg(org) {
try {
await org.save();
} catch (error) {
console.log('error ', error);
// Redirects to Orgs screen
this.router.transitionTo('scopes.scope', 'global');
throw new Error(error);
}
}
/**
* Creates Project, if fails redirects to Projects from Org empty list.
* @param {ScopeModel} org
* @param {ScopeModel} project
*/
async createProject(org, project) {
project.scopeID = org.id;
try {
await project.save();
} catch (error) {
// Redirects to Project screen
this.router.transitionTo('scopes.scope.scopes', project.scopeID);
throw new Error(error);
}
}
/**
* Creates a Target, if fails redirects to Targets empty list.
* @param {ScopeModel} project
* @param {TargetModel} target
* @param {string} targetAddress
* @param {string} targetPort
*/
async createTarget(project, target, targetAddress, targetPort) {
target.scopeID = project.id;
target.address = targetAddress;
target.default_port = targetPort;
try {
await target.save();
} catch (error) {
// Redirect to targets global
this.router.replaceWith('scopes.scope.targets', project.scopeID);
throw new Error(error);
}
}
/**
* Assigns default grants and scope to a role.
* @param {ScopeModel} org
* @param {ScopeModel} project
* @param {TargetModel} target
* @param {RoleModel} role
*/
async createGrants(org, project, target, role) {
role.scopeID = org.id;
role.grant_scope_id = project.id;
try {
await role.save();
await role.saveGrantStrings([
`type=target;actions=list`,
`ids=${target.id};actions=authorize-session`,
]);
} catch (error) {
// Redirect to targets that belong to scope
this.router.replaceWith('scopes.scope.targets', project.scopeID);
throw new Error(error);
}
}
/**
* Redirects user to index route for further processing.
*/
@action
doLater() {
this.router.transitionTo('index');
}
}
@@ -0,0 +1,29 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class OnboardingSuccessController extends Controller {
// =services
@service router;
// =actions
/**
* Redirect user to target created during onboarding.
* @param {object} model
*/
@action
showTargetList(model) {
const {
project: { id: scopeID },
target: { id: targetID },
} = model;
this.router.transitionTo('scopes.scope.targets.target', scopeID, targetID);
}
}
@@ -0,0 +1,74 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { notifyError } from 'core/decorators/notify';
import {
TYPE_AUTH_METHOD_PASSWORD,
TYPE_AUTH_METHOD_OIDC,
TYPE_AUTH_METHOD_LDAP,
} from 'api/models/auth-method';
export default class ScopesScopeAuthenticateMethodIndexController extends Controller {
// =services
@service session;
@service windowManager;
@service router;
// =actions
/**
* Delegates authentication to an authenticator of type matching the current
* auth method. If authentication fails, notifies with an alert.
*/
@action
@loading
@notifyError(() => 'errors.authentication-failed.title', {
sticky: false,
catch: true,
})
async authenticate(authMethod, creds) {
const scope = authMethod.scope;
const authenticatorName = `authenticator:${authMethod.type}`;
const requestCookies = false;
switch (authMethod.type) {
case TYPE_AUTH_METHOD_PASSWORD:
case TYPE_AUTH_METHOD_LDAP:
await this.session.authenticate(
authenticatorName,
creds,
requestCookies,
{ scope, authMethod },
);
this.router.transitionTo('index');
break;
case TYPE_AUTH_METHOD_OIDC:
await this.startOIDCAuthentication(authenticatorName, {
scope,
authMethod,
});
this.router.transitionTo('scopes.scope.authenticate.method.oidc');
break;
}
}
/**
* Kicks off the OIDC auth flow by opening a new window to the provider.
* @param {string} authenticatorName
* @param {object} options
*/
async startOIDCAuthentication(authenticatorName, options) {
const oidc = getOwner(this).lookup(authenticatorName);
// TODO: delegate this call from the session service so that we don't have
// to look up the authenticator directly
const json = await oidc.startAuthentication(options);
await this.windowManager.open(json.attributes.auth_url);
}
}
@@ -3,10 +3,12 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import Controller, { inject as controller } from '@ember/controller';
import { inject as service } from '@ember/service';
export default class ScopesScopeAuthenticateMethodOidcController extends Controller {
@controller('scopes/scope/authenticate/method/index') authMethod;
// =services
@service session;
@@ -0,0 +1,10 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller, { inject as controller } from '@ember/controller';
export default class ScopesScopeEditController extends Controller {
@controller('scopes/scope/index') scopes;
}
@@ -0,0 +1,77 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesScopeIndexController extends Controller {
// =services
@service router;
//= actions
/**
* Rollback changes on scope.
* @param {ScopeModel} scope
*/
@action
cancel(scope) {
const { isNew } = scope;
scope.rollbackAttributes();
if (isNew) this.router.transitionTo('scopes.scope');
}
/**
* Handle save scope.
* @param {ScopeModel} scope
*/
@action
@loading
@notifyError(({ message }) => message)
@notifySuccess(({ isNew }) =>
isNew ? 'notifications.create-success' : 'notifications.save-success',
)
async save(scope) {
const { isNew } = scope;
await scope.save();
await this.router.transitionTo('scopes.scope.edit', scope);
if (isNew) this.router.refresh();
}
/**
* Deletes the scope and redirects to index.
* @param {ScopeModel} scope
*/
@action
@loading
@confirm('questions.delete-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.delete-success')
async delete(scope) {
const { scopeID } = scope;
await scope.destroyRecord();
this.router.replaceWith('scopes.scope.scopes', scopeID);
}
/**
* Deletes the scope and redirects to index.
* @param {ScopeModel} scope
*/
@action
@loading
@confirm('resources.policy.questions.detach')
@notifyError(({ message }) => message)
@notifySuccess('resources.policy.messages.detach')
async detachStoragePolicy(scope) {
const { storage_policy_id } = scope;
scope.storage_policy_id = '';
await scope.detachStoragePolicy(storage_policy_id);
}
}
@@ -0,0 +1,10 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Controller, { inject as controller } from '@ember/controller';
export default class ScopesScopeScopesNewController extends Controller {
@controller('scopes/scope/index') scopes;
}
+1 -96
View File
@@ -5,9 +5,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { notifyError } from 'core/decorators/notify';
import { TYPE_TARGET_TCP } from 'api/models/target';
export default class OnboardingRoute extends Route {
@@ -16,7 +13,6 @@ export default class OnboardingRoute extends Route {
@service store;
@service session;
@service router;
@service intl;
// =methods
@@ -34,7 +30,7 @@ export default class OnboardingRoute extends Route {
/**
* List all scopes (orgs) under global.
* Creare org, project, target and roles
* Create org, project, target and roles
*/
async model() {
return {
@@ -59,95 +55,4 @@ export default class OnboardingRoute extends Route {
}),
};
}
// =actions
@action
@loading
@notifyError(function () {
return this.intl.t('errors.onboarding-failed.description');
})
async createResources(targetAddress, targetPort) {
const { org, project, target, role } = this.currentModel;
await this.createOrg(org);
await this.createProject(org, project);
await this.createTarget(project, target, targetAddress, targetPort);
await this.createGrants(org, project, target, role);
// Redirect to success
this.router.transitionTo('onboarding.success');
}
/**
* Creates Org, if fails, redirects to Orgs empty list
* @param {Object} org
*/
async createOrg(org) {
try {
await org.save();
} catch (error) {
// Redirects to Orgs screen
this.router.transitionTo('scopes.scope', 'global');
throw new Error(error);
}
}
/**
* Creates Project, if fails redirects to Projects from Org empty list.
* @param {Object} org
* @param {Object} project
*/
async createProject(org, project) {
project.scopeID = org.id;
try {
await project.save();
} catch (error) {
// Redirects to Project screen
this.router.transitionTo('scopes.scope.scopes', project.scopeID);
throw new Error(error);
}
}
/**
* Creates a Target, if fails redirects to Targets empyt list.
* @param {Object} project
* @param {Object} target
* @param {Object} targetAddress
* @param {Object} targetPort
*/
async createTarget(project, target, targetAddress, targetPort) {
target.scopeID = project.id;
target.address = targetAddress;
target.default_port = targetPort;
try {
await target.save();
} catch (error) {
// Redirect to targets global
this.router.replaceWith('scopes.scope.targets', project.scopeID);
throw new Error(error);
}
}
async createGrants(org, project, target, role) {
role.scopeID = org.id;
role.grant_scope_ids = [project.id];
try {
await role.save();
await role.saveGrantStrings([
`type=target;actions=list`,
`ids=${target.id};actions=authorize-session`,
]);
} catch (error) {
// Redirect to targets that belong to scope
this.router.replaceWith('scopes.scope.targets', project.scopeID);
throw new Error(error);
}
}
@action
async doLater() {
this.router.transitionTo('index');
}
}
+2 -12
View File
@@ -5,15 +5,14 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class OnboardingSuccessRoute extends Route {
// =services
@service router;
@service session;
@service('browser/window') window;
// =methods
model() {
const model = this.modelFor('onboarding');
return {
@@ -21,13 +20,4 @@ export default class OnboardingSuccessRoute extends Route {
clusterUrl: this.window.origin,
};
}
@action
showTargetList(scope) {
const {
project: { id: scopeID },
target: { id: targetID },
} = scope;
this.router.transitionTo('scopes.scope.targets.target', scopeID, targetID);
}
}
-19
View File
@@ -6,10 +6,6 @@
import Route from '@ember/routing/route';
import { A } from '@ember/array';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
export default class ScopesRoute extends Route {
// =services
@@ -41,19 +37,4 @@ export default class ScopesRoute extends Route {
})
.catch(() => A([]));
}
/**
* Deletes the scope and redirects to index.
* @param {Model} scope
*/
@action
@loading
@confirm('resources.policy.questions.detach')
@notifyError(({ message }) => message)
@notifySuccess('resources.policy.messages.detach')
async detachStoragePolicy(scope) {
const { storage_policy_id } = scope;
scope.storage_policy_id = '';
await scope.detachStoragePolicy(storage_policy_id);
}
}
-56
View File
@@ -6,21 +6,13 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { A } from '@ember/array';
import { action } from '@ember/object';
import { next } from '@ember/runloop';
import { loading } from 'ember-loading';
import { confirm } from 'core/decorators/confirm';
import { notifySuccess, notifyError } from 'core/decorators/notify';
import { TYPE_SCOPE_GLOBAL, TYPE_SCOPE_ORG } from 'api/models/scope';
export default class ScopesScopeRoute extends Route {
// =services
@service store;
@service intl;
@service session;
@service scope;
@service router;
// =methods
@@ -105,52 +97,4 @@ export default class ScopesScopeRoute extends Route {
/* eslint-disable-next-line ember/no-controller-access-in-routes */
this.controller.setProperties({ scopes });
}
// =actions
/**
* Rollback changes on scope.
* @param {Model} scope
*/
@action
cancel(scope) {
const { isNew } = scope;
next(() => {
scope.rollbackAttributes();
if (isNew) this.router.transitionTo('scopes.scope');
});
}
/**
* Handle save scope.
* @param {Model} scope
* @param {Event} e
*/
@action
@loading
@notifyError(({ message }) => message)
@notifySuccess(({ isNew }) =>
isNew ? 'notifications.create-success' : 'notifications.save-success',
)
async save(scope) {
const { isNew } = scope;
await scope.save();
await this.router.transitionTo('scopes.scope.edit', scope);
if (isNew) this.refresh();
}
/**
* Deletes the scope and redirects to index.
* @param {Model} scope
*/
@action
@loading
@confirm('questions.delete-confirm')
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.delete-success')
async delete(scope) {
const { scopeID } = scope;
await scope.destroyRecord();
this.router.replaceWith('scopes.scope.scopes', scopeID);
}
}
@@ -5,24 +5,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { notifyError } from 'core/decorators/notify';
import {
TYPE_AUTH_METHOD_PASSWORD,
TYPE_AUTH_METHOD_OIDC,
TYPE_AUTH_METHOD_LDAP,
} from 'api/models/auth-method';
export default class ScopesScopeAuthenticateMethodRoute extends Route {
// =services
@service store;
@service session;
@service intl;
@service windowManager;
@service router;
// =methods
@@ -35,55 +22,4 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route {
const adapterOptions = { scopeID: this.modelFor('scopes.scope').id };
return this.store.peekRecord('auth-method', id, adapterOptions);
}
/**
* Kicks off the OIDC auth flow by opening a new window to the provider.
* @param {string} authenticatorName
* @param {object} options
*/
async startOIDCAuthentication(authenticatorName, options) {
const oidc = getOwner(this).lookup(authenticatorName);
// TODO: delegate this call from the session service so that we don't have
// to look up the authenticator directly
const json = await oidc.startAuthentication(options);
await this.windowManager.open(json.attributes.auth_url);
}
// =actions
/**
* Delegates authentication to an authenticator of type matching the current
* auth method. If authentication fails, notifies with an alert.
*/
@action
@loading
@notifyError(() => 'errors.authentication-failed.title', {
sticky: false,
catch: true,
})
async authenticate(creds) {
const scope = this.modelFor('scopes.scope');
const authMethod = this.modelFor('scopes.scope.authenticate.method');
const authenticatorName = `authenticator:${authMethod.type}`;
const requestCookies = false;
switch (authMethod.type) {
case TYPE_AUTH_METHOD_PASSWORD:
case TYPE_AUTH_METHOD_LDAP:
await this.session.authenticate(
authenticatorName,
creds,
requestCookies,
{ scope, authMethod },
);
this.router.transitionTo('index');
break;
case TYPE_AUTH_METHOD_OIDC:
await this.startOIDCAuthentication(authenticatorName, {
scope,
authMethod,
});
this.router.transitionTo('scopes.scope.authenticate.method.oidc');
break;
}
}
}
+2 -2
View File
@@ -12,8 +12,8 @@
<p>{{t 'onboarding.add-target.descriptions.setup-first-target'}}</p>
<Form::Onboarding
@model={{@model}}
@submit={{route-action 'createResources'}}
@cancel={{route-action 'doLater'}}
@submit={{fn this.createResources @model}}
@cancel={{this.doLater}}
/>
</:body>
</Rose::Frame>
@@ -86,7 +86,7 @@
<Hds::Button
@text={{t 'actions.done'}}
data-test-onboarding-done-button
{{on 'click' (route-action 'showTargetList' @model)}}
{{on 'click' (fn this.showTargetList @model)}}
/>
</:body>
</Rose::Frame>
@@ -5,6 +5,6 @@
<Form::Authenticate
@model={{@model}}
@submit={{route-action 'authenticate'}}
@submit={{fn this.authenticate @model}}
@disabled={{(is-loading)}}
/>
@@ -12,7 +12,7 @@
<p>{{t 'resources.auth-method.messages.pending.description'}}</p>
<p>
{{t 'resources.auth-method.questions.no-see-window'}}
<a href='#' role='button' {{on 'click' (route-action 'authenticate')}}>
<a href='#' role='button' {{on 'click' this.authMethod.authenticate}}>
{{t 'actions.retry'}}
</a>
</p>
+4 -4
View File
@@ -64,7 +64,7 @@
<dropdown.button
@style='danger'
@disabled={{@model.canSave}}
{{on 'click' (route-action 'delete' @model)}}
{{on 'click' (fn this.scopes.delete @model)}}
>
{{#if @model.isOrg}}
{{t 'resources.org.actions.delete'}}
@@ -82,8 +82,8 @@
{{#if (can 'read model' @model)}}
<Form::Scope
@model={{@model}}
@submit={{route-action 'save' @model}}
@cancel={{route-action 'cancel' @model}}
@submit={{fn this.scopes.save @model}}
@cancel={{fn this.scopes.cancel @model}}
/>
{{else}}
<Rose::Layout::Centered>
@@ -176,7 +176,7 @@
@color='critical'
{{on
'click'
(route-action 'detachStoragePolicy' @model)
(fn this.scopes.detachStoragePolicy @model)
}}
/>
</Hds::Dropdown>
@@ -41,8 +41,8 @@
<page.body>
<Form::Scope
@model={{@model}}
@submit={{route-action 'save' @model}}
@cancel={{route-action 'cancel' @model}}
@submit={{fn this.scopes.save @model}}
@cancel={{fn this.scopes.cancel @model}}
/>
</page.body>
@@ -0,0 +1,95 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { currentURL, waitUntil, visit } from '@ember/test-helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_TARGET_TCP } from 'api/models/target';
module('Unit | Controller | onboarding/index', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
let store;
let controller;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
target: null,
role: null,
};
const urls = {
onboarding: null,
success: null,
globalScope: null,
};
hooks.beforeEach(async function () {
authenticateSession({});
store = this.owner.lookup('service:store');
controller = this.owner.lookup('controller:onboarding/index');
instances.scopes.global = this.server.create('scope', {
id: 'global',
type: 'global',
});
urls.onboarding = '/onboarding';
urls.success = '/onboarding/success';
urls.globalScope = '/scopes/global/scopes';
});
test('it exists', function (assert) {
assert.ok(controller);
});
test('createResources action creates and saves resources', async function (assert) {
await visit(urls.onboarding);
const global = await store.findRecord('scope', instances.scopes.global.id);
const org = store.createRecord('scope', { type: 'org' });
org.scopeModel = global;
const project = store.createRecord('scope', { type: 'project' });
const target = store.createRecord('target', {
type: TYPE_TARGET_TCP,
scope: project,
});
const role = store.createRecord('role', { scope: org });
await controller.createResources(
{ org, project, target, role },
'localhost',
'22',
);
assert.strictEqual(project.scopeID, org.id);
assert.strictEqual(target.address, 'localhost');
assert.strictEqual(target.default_port, 22);
assert.strictEqual(target.scopeID, project.id);
assert.strictEqual(role.scopeID, org.id);
assert.deepEqual(role.grant_strings, [
'type=target;actions=list',
'ids=1;actions=authorize-session',
]);
assert.strictEqual(currentURL(), urls.success);
});
test('doLater action redirects user to correct page', async function (assert) {
await visit(urls.onboarding);
controller.doLater();
await waitUntil(() => currentURL() === urls.globalScope);
assert.strictEqual(currentURL(), urls.globalScope);
});
});
@@ -0,0 +1,78 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { currentURL, waitUntil, visit } from '@ember/test-helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { authenticateSession } from 'ember-simple-auth/test-support';
module('Unit | Controller | onboarding/success', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
let controller;
let store;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
target: null,
};
const urls = {
success: null,
target: null,
};
hooks.beforeEach(async function () {
authenticateSession({});
controller = this.owner.lookup('controller:onboarding/success');
store = this.owner.lookup('service:store');
instances.scopes.global = this.server.create('scope', {
id: 'global',
type: 'global',
});
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: { id: instances.scopes.org.id, type: 'org' },
});
instances.target = this.server.create('target', {
scope: instances.scopes.project,
});
urls.success = '/onboarding/success';
urls.target = `/scopes/${instances.scopes.project.id}/targets/${instances.target.id}`;
});
test('it exists', function (assert) {
assert.ok(controller);
});
test('showTargetList action redirects user a target', async function (assert) {
await visit(urls.success);
const target = await store.findRecord('target', instances.target.id);
const project = await store.findRecord(
'scope',
instances.scopes.project.id,
);
const model = { target, project };
controller.showTargetList(model);
await waitUntil(() => currentURL() === urls.target);
assert.strictEqual(currentURL(), urls.target);
});
});
@@ -0,0 +1,91 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { currentURL, visit, waitUntil } from '@ember/test-helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_AUTH_METHOD_PASSWORD } from 'api/models/auth-method';
module(
'Unit | Controller | scopes/scope/authenticate/method/index',
function (hooks) {
setupTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
let controller;
let store;
let session;
const instances = {
scopes: {
global: null,
},
authMethod: null,
};
const urls = {
globalScope: null,
authenticate: null,
};
hooks.beforeEach(async function () {
authenticateSession({});
controller = this.owner.lookup(
'controller:scopes/scope/authenticate/method/index',
);
store = this.owner.lookup('service:store');
session = this.owner.lookup('service:session');
instances.scopes.global = this.server.create('scope', {
id: 'global',
type: 'global',
});
instances.authMethod = this.server.create('auth-method', {
scope: instances.scopes.global,
type: TYPE_AUTH_METHOD_PASSWORD,
});
urls.globalScope = '/scopes/global/scopes';
urls.authenticate = `scopes/global/authenticate/${instances.authMethod}`;
});
test('it exists', function (assert) {
assert.ok(controller);
});
test('authenticate action saves login information and redirects to correct page', async function (assert) {
await visit(urls.authenticate);
const identification = 'admin123';
const authMethod = await store.findRecord(
'auth-method',
instances.authMethod.id,
);
const { authenticator: authBefore, username: usernameBefore } =
session.data.authenticated;
assert.notOk(usernameBefore);
assert.strictEqual(authBefore, 'authenticator:test');
await controller.authenticate(authMethod, {
identification,
password: 'password',
});
await waitUntil(() => currentURL() === urls.globalScope);
const { authenticator: authAfter, username: usernameAfter } =
session.data.authenticated;
assert.strictEqual(currentURL(), urls.globalScope);
assert.strictEqual(
authAfter,
`authenticator:${instances.authMethod.type}`,
);
assert.strictEqual(usernameAfter, identification);
});
},
);
@@ -11,12 +11,12 @@ module(
function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:scopes/scope/authenticate/method/oidc',
);
assert.ok(controller);
assert.ok(controller.authMethod);
});
},
);
@@ -0,0 +1,17 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | scopes/scope/edit', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:scopes/scope/edit');
assert.ok(controller);
assert.ok(controller.scopes);
});
});
@@ -0,0 +1,98 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { visit } from '@ember/test-helpers';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
import { authenticateSession } from 'ember-simple-auth/test-support';
module('Unit | Controller | scopes/scope/index', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
setupIndexedDb(hooks);
let store;
let controller;
let getScopeCount;
const instances = {
scopes: {
global: null,
org: null,
},
policy: null,
};
const urls = {
globalScope: null,
};
hooks.beforeEach(function () {
authenticateSession({});
store = this.owner.lookup('service:store');
controller = this.owner.lookup('controller:scopes/scope/index');
instances.scopes.global = this.server.create('scope', { id: 'global' });
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: { id: 'global', type: 'global' },
});
instances.policy = this.server.create('policy', {
scope: instances.scopes.org,
});
urls.globalScope = `/scopes/global/scopes`;
getScopeCount = () => this.server.schema.scopes.all().models.length;
});
test('it exists', function (assert) {
assert.ok(controller);
});
test('cancel action rolls-back changes on the specified model', async function (assert) {
await visit(urls.globalScope);
const org = await store.findRecord('scope', instances.scopes.org.id);
org.name = 'test';
assert.strictEqual(org.name, 'test');
await controller.cancel(org);
assert.notEqual(org.name, 'test');
});
test('save action saves changes on the specified model', async function (assert) {
await visit(urls.globalScope);
const org = await store.findRecord('scope', instances.scopes.org.id);
org.name = 'test';
await controller.save(org);
assert.strictEqual(org.name, 'test');
});
test('delete action destroys specified model', async function (assert) {
const org = await store.findRecord('scope', instances.scopes.org.id);
const scopeCount = getScopeCount();
await controller.delete(org);
assert.strictEqual(getScopeCount(), scopeCount - 1);
});
test('detachStoragePolicy action removes policy from model', async function (assert) {
instances.scopes.org.update({ storage_policy_id: instances.policy.id });
const org = await store.findRecord('scope', instances.scopes.org.id);
assert.strictEqual(org.storage_policy_id, instances.policy.id);
await controller.detachStoragePolicy(org);
assert.notOk(org.storage_policy_id);
});
});
@@ -0,0 +1,17 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Controller | scopes/scope/scopes/new', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:scopes/scope/scopes/new');
assert.ok(controller);
assert.ok(controller.scopes);
});
});