Merge branch 'main' into credentials-store-icon-update
This commit is contained in:
13 files changed
+283
-5
No files matched your search
@@ -29,7 +29,7 @@
|
|||||||
"ember-cli-mirage": "^1.1.8",
|
"ember-cli-mirage": "^1.1.8",
|
||||||
"ember-data": "^3.20.0",
|
"ember-data": "^3.20.0",
|
||||||
"ember-data-model-fragments": "5.0.0-beta.2",
|
"ember-data-model-fragments": "5.0.0-beta.2",
|
||||||
"ember-fetch": "^8.0.1"
|
"ember-fetch": "^8.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ember/optional-features": "^2.0.0",
|
"@ember/optional-features": "^2.0.0",
|
||||||
|
|||||||
@@ -267,6 +267,11 @@ target:
|
|||||||
add:
|
add:
|
||||||
title: Add Host Sets
|
title: Add Host Sets
|
||||||
description: Select host sets to assign to this target.
|
description: Select host sets to assign to this target.
|
||||||
|
credential-library:
|
||||||
|
messages:
|
||||||
|
welcome:
|
||||||
|
title: Welcome to Credential Libraries
|
||||||
|
description: No credential libraries in this target.
|
||||||
credential-store:
|
credential-store:
|
||||||
title: Credential Store
|
title: Credential Store
|
||||||
title_plural: Credential Stores
|
title_plural: Credential Stores
|
||||||
@@ -282,6 +287,7 @@ credential-library:
|
|||||||
description: A credential library is a resource that provides credentials.
|
description: A credential library is a resource that provides credentials.
|
||||||
actions:
|
actions:
|
||||||
create: New Credential Library
|
create: New Credential Library
|
||||||
|
delete: Delete Credential Library
|
||||||
messages:
|
messages:
|
||||||
none:
|
none:
|
||||||
title: No Credential Libraries Available
|
title: No Credential Libraries Available
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import Controller from '@ember/controller';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class ScopesScopeTargetsTargetCredentialLibrariesController extends Controller {
|
||||||
|
// =services
|
||||||
|
|
||||||
|
@service intl;
|
||||||
|
|
||||||
|
// =attributes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translated credential library breadcrumb
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get breadCrumb() {
|
||||||
|
return this.intl.t('resources.credential-library.title_plural');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ Router.map(function () {
|
|||||||
this.route('target', { path: ':target_id' }, function () {
|
this.route('target', { path: ':target_id' }, function () {
|
||||||
this.route('host-sets', function () {});
|
this.route('host-sets', function () {});
|
||||||
this.route('add-host-sets');
|
this.route('add-host-sets');
|
||||||
|
this.route('credential-libraries');
|
||||||
});
|
});
|
||||||
this.route('new');
|
this.route('new');
|
||||||
});
|
});
|
||||||
|
|||||||
+18
@@ -1,6 +1,7 @@
|
|||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import loading from 'ember-loading/decorator';
|
import loading from 'ember-loading/decorator';
|
||||||
|
import { confirm } from 'core/decorators/confirm';
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
|
|
||||||
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
|
export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrariesRoute extends Route {
|
||||||
@@ -50,4 +51,21 @@ export default class ScopesScopeCredentialStoresCredentialStoreCredentialLibrari
|
|||||||
);
|
);
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle delete of a credential library
|
||||||
|
* @param {CredentialLibraryModel} credentialLibrary
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
@loading
|
||||||
|
@confirm('questions.delete-confirm')
|
||||||
|
@notifyError(({ message }) => message, { catch: true })
|
||||||
|
@notifySuccess('notifications.delete-success')
|
||||||
|
async delete(credentialLibrary) {
|
||||||
|
await credentialLibrary.destroyRecord();
|
||||||
|
await this.replaceWith(
|
||||||
|
'scopes.scope.credential-stores.credential-store.credential-libraries'
|
||||||
|
);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { hash, all } from 'rsvp';
|
||||||
|
|
||||||
|
export default class ScopesScopeTargetsTargetCredentialLibrariesRoute extends Route {
|
||||||
|
// =methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all credential libraries under the current target.
|
||||||
|
* @return {Promise{[CredentialLibraryModel]}}
|
||||||
|
*/
|
||||||
|
beforeModel() {
|
||||||
|
const { application_credential_library_ids: credential_libraries } =
|
||||||
|
this.modelFor('scopes.scope.targets.target');
|
||||||
|
return hash({
|
||||||
|
credentialLibraries: all(
|
||||||
|
credential_libraries.map((credential_library) =>
|
||||||
|
this.store.findRecord(
|
||||||
|
'credential-library',
|
||||||
|
credential_library.value,
|
||||||
|
{ reload: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previously loaded target instance.
|
||||||
|
* @return {TargetModel}
|
||||||
|
*/
|
||||||
|
model() {
|
||||||
|
return this.modelFor('scopes.scope.targets.target');
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
<Rose::Dropdown
|
||||||
|
@text={{t 'actions.manage'}}
|
||||||
|
@dropdownRight={{true}}
|
||||||
|
as |dropdown|
|
||||||
|
>
|
||||||
|
<dropdown.button
|
||||||
|
@style='danger'
|
||||||
|
@disabled={{@model.canSave}}
|
||||||
|
{{on 'click' (route-action 'delete' @model)}}
|
||||||
|
>
|
||||||
|
{{t 'resources.credential-library.actions.delete'}}
|
||||||
|
</dropdown.button>
|
||||||
|
</Rose::Dropdown>
|
||||||
@@ -5,4 +5,9 @@
|
|||||||
<nav.link @route='scopes.scope.targets.target.host-sets'>
|
<nav.link @route='scopes.scope.targets.target.host-sets'>
|
||||||
{{t 'resources.host-set.title_plural'}}
|
{{t 'resources.host-set.title_plural'}}
|
||||||
</nav.link>
|
</nav.link>
|
||||||
|
{{#if (feature-flag 'credential-store')}}
|
||||||
|
<nav.link @route='scopes.scope.targets.target.credential-libraries'>
|
||||||
|
{{t 'resources.credential-library.title_plural'}}
|
||||||
|
</nav.link>
|
||||||
|
{{/if}}
|
||||||
</Rose::Nav::Tabs>
|
</Rose::Nav::Tabs>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{{page-title (t 'resources.credential-library.title_plural')}}
|
||||||
|
|
||||||
|
{{#if @model.credentialLibraries}}
|
||||||
|
|
||||||
|
<Rose::Table as |table|>
|
||||||
|
<table.header as |header|>
|
||||||
|
<header.row as |row|>
|
||||||
|
<row.headerCell>{{t 'form.name.label'}}</row.headerCell>
|
||||||
|
<row.headerCell>{{t 'form.id.label'}}</row.headerCell>
|
||||||
|
</header.row>
|
||||||
|
</table.header>
|
||||||
|
<table.body as |body|>
|
||||||
|
{{#each @model.credentialLibraries as |credentialLibrary|}}
|
||||||
|
<body.row as |row|>
|
||||||
|
<row.headerCell>
|
||||||
|
<LinkTo
|
||||||
|
@route='scopes.scope.credential-stores.credential-store.credential-libraries.credential-library'
|
||||||
|
@models={{array
|
||||||
|
credentialLibrary.credential_store_id
|
||||||
|
credentialLibrary.id
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{credentialLibrary.displayName}}
|
||||||
|
</LinkTo>
|
||||||
|
<p>{{credentialLibrary.description}}</p>
|
||||||
|
</row.headerCell>
|
||||||
|
<row.cell>
|
||||||
|
<Copyable
|
||||||
|
@text={{credentialLibrary.id}}
|
||||||
|
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||||
|
@acknowledgeText={{t 'states.copied'}}
|
||||||
|
>
|
||||||
|
<code>{{credentialLibrary.id}}</code>
|
||||||
|
</Copyable>
|
||||||
|
</row.cell>
|
||||||
|
</body.row>
|
||||||
|
{{/each}}
|
||||||
|
</table.body>
|
||||||
|
</Rose::Table>
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
|
||||||
|
<Rose::Layout::Centered>
|
||||||
|
<Rose::Message
|
||||||
|
@title={{t 'resources.target.credential-library.messages.welcome.title'}}
|
||||||
|
as |message|
|
||||||
|
>
|
||||||
|
<message.description>
|
||||||
|
{{t 'resources.target.credential-library.messages.welcome.description'}}
|
||||||
|
</message.description>
|
||||||
|
</Rose::Message>
|
||||||
|
</Rose::Layout::Centered>
|
||||||
|
|
||||||
|
{{/if}}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { visit, click } from '@ember/test-helpers';
|
||||||
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
|
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||||
|
import { resolve, reject } from 'rsvp';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
module('Acceptance | credential-library', function (hooks) {
|
||||||
|
setupApplicationTest(hooks);
|
||||||
|
setupMirage(hooks);
|
||||||
|
|
||||||
|
let getCredentialLibraryCount;
|
||||||
|
|
||||||
|
const instances = {
|
||||||
|
scopes: {
|
||||||
|
global: null,
|
||||||
|
org: null,
|
||||||
|
project: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const urls = {
|
||||||
|
globalScope: null,
|
||||||
|
orgScope: null,
|
||||||
|
projectScope: null,
|
||||||
|
credentialStores: null,
|
||||||
|
credentialStore: null,
|
||||||
|
credentialLibrary: null,
|
||||||
|
credentialLibraries: null,
|
||||||
|
newCredentialLibrary: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
// Generate resources
|
||||||
|
instances.scopes.global = this.server.create('scope', { id: '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.credentialStore = this.server.create('credential-store', {
|
||||||
|
scope: instances.scopes.project,
|
||||||
|
});
|
||||||
|
instances.credentialLibrary = this.server.create('credential-library', {
|
||||||
|
scope: instances.scopes.project,
|
||||||
|
});
|
||||||
|
// Generate route URLs for resources
|
||||||
|
urls.globalScope = `/scopes/global/scopes`;
|
||||||
|
urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`;
|
||||||
|
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
|
||||||
|
urls.credentialStores = `${urls.projectScope}/credential-stores`;
|
||||||
|
urls.credentialStore = `${urls.credentialStores}/${instances.credentialStore.id}`;
|
||||||
|
urls.credentialLibraries = `${urls.credentialStore}/credential-libraries`;
|
||||||
|
urls.credentialLibrary = `${urls.credentialLibraries}/${instances.credentialLibrary.id}`;
|
||||||
|
// Generate resource couner
|
||||||
|
getCredentialLibraryCount = () =>
|
||||||
|
this.server.schema.credentialLibraries.all().models.length;
|
||||||
|
authenticateSession({});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can delete credential library', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
const count = getCredentialLibraryCount();
|
||||||
|
await visit(urls.credentialLibrary);
|
||||||
|
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
|
||||||
|
assert.equal(getCredentialLibraryCount(), count - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can accept delete credential library via dialog', async function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
confirmService.confirm = sinon.fake.returns(resolve());
|
||||||
|
const count = getCredentialLibraryCount();
|
||||||
|
await visit(urls.credentialLibrary);
|
||||||
|
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
|
||||||
|
assert.equal(getCredentialLibraryCount(), count - 1);
|
||||||
|
assert.ok(confirmService.confirm.calledOnce);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot cancel delete credential library via dialog', async function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
confirmService.confirm = sinon.fake.returns(reject());
|
||||||
|
const count = getCredentialLibraryCount();
|
||||||
|
await visit(urls.credentialLibrary);
|
||||||
|
await click('.rose-layout-page-actions .rose-dropdown-button-danger');
|
||||||
|
assert.equal(getCredentialLibraryCount(), count);
|
||||||
|
assert.ok(confirmService.confirm.calledOnce);
|
||||||
|
});
|
||||||
|
});
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module(
|
||||||
|
'Unit | Controller | scopes/scope/targets/target/credential-libraries',
|
||||||
|
function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
// TODO: Replace this with your real tests.
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
let controller = this.owner.lookup(
|
||||||
|
'controller:scopes/scope/targets/target/credential-libraries'
|
||||||
|
);
|
||||||
|
assert.ok(controller);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupTest } from 'ember-qunit';
|
||||||
|
|
||||||
|
module(
|
||||||
|
'Unit | Route | scopes/scope/targets/target/credential-libraries',
|
||||||
|
function (hooks) {
|
||||||
|
setupTest(hooks);
|
||||||
|
|
||||||
|
test('it exists', function (assert) {
|
||||||
|
let route = this.owner.lookup(
|
||||||
|
'route:scopes/scope/targets/target/credential-libraries'
|
||||||
|
);
|
||||||
|
assert.ok(route);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -9248,10 +9248,10 @@ ember-feature-flags@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ember-cli-babel "^7.11.1"
|
ember-cli-babel "^7.11.1"
|
||||||
|
|
||||||
ember-fetch@^8.0.1:
|
ember-fetch@^8.1.0:
|
||||||
version "8.0.5"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/ember-fetch/-/ember-fetch-8.0.5.tgz#0ed6257b3158473f662e72831cab223010c5ec50"
|
resolved "https://registry.yarnpkg.com/ember-fetch/-/ember-fetch-8.1.0.tgz#6228f49baaf6f435af1e5c767e19ef638ff28883"
|
||||||
integrity sha512-tKwSWZ2l4jKynqUuF8qgwiCfzmnRWDDO2j5l0Lrn4TgZo8YXbdM3DfgheLSZd+aybBFEAtXBgKH0qaTox28LFg==
|
integrity sha512-797IPPuXrWGAecsAuCPH6+2pJCcawy7+CiRrtoe1qLhXJ6Ue0Lxu4loXxHHQNCxRvvUvx8tTDuP63sOKfi3xug==
|
||||||
dependencies:
|
dependencies:
|
||||||
abortcontroller-polyfill "^1.7.1"
|
abortcontroller-polyfill "^1.7.1"
|
||||||
broccoli-concat "^4.2.5"
|
broccoli-concat "^4.2.5"
|
||||||
|
|||||||
Reference in new issue
Block a user