diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 008ed2743..1b495c0b2 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -456,6 +456,14 @@ export default function () { return new Response(200); }); + // credential-stores + + this.get( + '/credential-stores', + ({ credentialStores }, { queryParams: { scope_id: scopeId } }) => + credentialStores.where({ scopeId }) + ); + /* Uncomment the following line and the Response import above * Then change the response code to simulate error responses. * this.get('/scopes', () => new Response(505)); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index dd7ea9359..2c9712a67 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -267,4 +267,7 @@ target: add: title: Add Host Sets description: Select host sets to assign to this target. - secret: ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ +credential-store: + title: Credential Store + title_plural: Credential Stores + description: A credential stores is a collection of credentials and credential libraries. diff --git a/ui/admin/app/controllers/scopes/scope/credential-stores.js b/ui/admin/app/controllers/scopes/scope/credential-stores.js new file mode 100644 index 000000000..315651ba1 --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/credential-stores.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeCredentialStoresController extends Controller { + // =services + + @service intl; + + // = attributes + /** + * Translated breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.credential-store.title_plural'); + } +} diff --git a/ui/admin/app/router.js b/ui/admin/app/router.js index c63c9a627..4d723cab8 100644 --- a/ui/admin/app/router.js +++ b/ui/admin/app/router.js @@ -86,6 +86,9 @@ Router.map(function () { }); }); }); + this.route('credential-stores', function () { + this.route('credential-store', { path: ':credential_store_id' }); + }); }); }); diff --git a/ui/admin/app/routes/scopes/scope/credential-stores.js b/ui/admin/app/routes/scopes/scope/credential-stores.js new file mode 100644 index 000000000..7f75dd4fa --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/credential-stores.js @@ -0,0 +1,12 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeCredentialStoresRoute extends Route { + /** + * Load all credential stores under current scope + * @returns {Promise[CredentialStoreModel]} + */ + async model() { + const { id: scope_id } = this.modelFor('scopes.scope'); + return this.store.query('credential-store', { scope_id }); + } +} diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js new file mode 100644 index 000000000..82fa9d62c --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/credential-stores/credential-store.js @@ -0,0 +1,11 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeCredentialStoresCredentialStoreRoute extends Route { + /** + * Load a specific credential store in current scope + * @return {Promise[CredentialStoreModel]} + */ + async model({ credential_store_id }) { + return this.route.findRecord('credential-store', credential_store_id); + } +} diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/index.js b/ui/admin/app/routes/scopes/scope/credential-stores/index.js new file mode 100644 index 000000000..99ced4658 --- /dev/null +++ b/ui/admin/app/routes/scopes/scope/credential-stores/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeCredentialStoresIndexRoute extends Route {} diff --git a/ui/admin/app/templates/scopes/scope.hbs b/ui/admin/app/templates/scopes/scope.hbs index b25624cb9..b0040f3e7 100644 --- a/ui/admin/app/templates/scopes/scope.hbs +++ b/ui/admin/app/templates/scopes/scope.hbs @@ -83,6 +83,12 @@ {{t 'resources.host-catalog.title_plural'}} + {{#if (feature-flag 'credential-store')}} + + + {{t 'resources.credential-store.title_plural'}} + + {{/if}} {{/if}} diff --git a/ui/admin/app/templates/scopes/scope/credential-stores.hbs b/ui/admin/app/templates/scopes/scope/credential-stores.hbs new file mode 100644 index 000000000..aabd1104b --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores.hbs @@ -0,0 +1,3 @@ +{{page-title (t 'resources.credential-store.title_plural')}} + +{{outlet}} \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs new file mode 100644 index 000000000..637b45528 --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores/credential-store.hbs @@ -0,0 +1,2 @@ +{{page-title 'CredentialStore'}} +{{outlet}} \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs new file mode 100644 index 000000000..585e076c2 --- /dev/null +++ b/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs @@ -0,0 +1,65 @@ + + + + + + +

+ {{t 'resources.credential-store.title_plural'}} +

+ {{t 'resources.credential-store.description'}} +
+ + {{!-- + {{#if @model}} + + {{t 'titles.new'}} + + {{/if}} + --}} + + + {{#if @model}} + + + + {{t 'form.name.label'}} + {{t 'form.type.label'}} + {{t 'form.id.label'}} + + + + + {{#each @model as |credentialStore|}} + + + + {{credentialStore.displayName}} + +

{{credentialStore.description}}

+
+ + {{credentialStore.type}} + + + + {{credentialStore.id}} + + +
+ {{/each}} +
+
+ {{/if}} +
+
\ No newline at end of file diff --git a/ui/admin/tests/acceptance/credential-stores-test.js b/ui/admin/tests/acceptance/credential-stores-test.js new file mode 100644 index 000000000..b62c0f3fe --- /dev/null +++ b/ui/admin/tests/acceptance/credential-stores-test.js @@ -0,0 +1,48 @@ +import { module, test } from 'qunit'; +import { visit, currentURL } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; + +module('Acceptance | credential-stores', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + project: null, + }, + }; + + const urls = { + globalScope: null, + credentialStores: 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' }, + }); + // 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`; + }); + + test('visiting /credential-stores', async function (assert) { + assert.expect(1); + await visit(urls.credentialStores); + await a11yAudit(); + assert.equal(currentURL(), urls.credentialStores); + }); +}); diff --git a/ui/admin/tests/unit/controllers/scopes/scope/credential-stores-test.js b/ui/admin/tests/unit/controllers/scopes/scope/credential-stores-test.js new file mode 100644 index 000000000..4667d6b43 --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/credential-stores-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/credential-stores', function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/credential-stores' + ); + assert.ok(controller); + }); +}); diff --git a/ui/admin/tests/unit/routes/scopes/scope/credential-stores-test.js b/ui/admin/tests/unit/routes/scopes/scope/credential-stores-test.js new file mode 100644 index 000000000..002668809 --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/credential-stores-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/credential-stores', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/credential-stores'); + assert.ok(route); + }); +}); diff --git a/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store-test.js b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store-test.js new file mode 100644 index 000000000..3ee3434c8 --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/credential-store-test.js @@ -0,0 +1,16 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Route | scopes/scope/credential-stores/credential-store', + function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:scopes/scope/credential-stores/credential-store' + ); + assert.ok(route); + }); + } +); diff --git a/ui/admin/tests/unit/routes/scopes/scope/credential-stores/index-test.js b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/index-test.js new file mode 100644 index 000000000..6bc2ed3c6 --- /dev/null +++ b/ui/admin/tests/unit/routes/scopes/scope/credential-stores/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/credential-stores/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/credential-stores/index'); + assert.ok(route); + }); +});