diff --git a/ui/admin/app/controllers/scopes/scope/credential-stores/index.js b/ui/admin/app/controllers/scopes/scope/credential-stores/index.js
index 33a6ea028..e525e6296 100644
--- a/ui/admin/app/controllers/scopes/scope/credential-stores/index.js
+++ b/ui/admin/app/controllers/scopes/scope/credential-stores/index.js
@@ -21,12 +21,21 @@ export default class ScopesScopeCredentialStoresIndexController extends Controll
// =attributes
- queryParams = ['search', { types: { type: 'array' } }, 'page', 'pageSize'];
+ queryParams = [
+ 'search',
+ { types: { type: 'array' } },
+ 'page',
+ 'pageSize',
+ 'sortAttribute',
+ 'sortDirection',
+ ];
@tracked search;
@tracked types = [];
@tracked page = 1;
@tracked pageSize = 10;
+ @tracked sortAttribute;
+ @tracked sortDirection;
get credStoreTypeOptions() {
return TYPES_CREDENTIAL_STORE.map((type) => ({
@@ -176,4 +185,16 @@ export default class ScopesScopeCredentialStoresIndexController extends Controll
'scopes.scope.credential-stores.credential-store.worker-filter',
);
}
+
+ /**
+ * Set sort values and sets page to 1
+ * @param {string} sortBy
+ * @param {string} sortOrder
+ */
+ @action
+ onSort(sortBy, sortOrder) {
+ this.sortAttribute = sortBy;
+ this.sortDirection = sortOrder;
+ this.page = 1;
+ }
}
diff --git a/ui/admin/app/routes/scopes/scope/credential-stores/index.js b/ui/admin/app/routes/scopes/scope/credential-stores/index.js
index 92e04cef8..7af327672 100644
--- a/ui/admin/app/routes/scopes/scope/credential-stores/index.js
+++ b/ui/admin/app/routes/scopes/scope/credential-stores/index.js
@@ -30,6 +30,14 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route {
pageSize: {
refreshModel: true,
},
+ sortAttribute: {
+ refreshModel: true,
+ replace: true,
+ },
+ sortDirection: {
+ refreshModel: true,
+ replace: true,
+ },
};
// =methods
@@ -45,7 +53,15 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route {
}
retrieveData = restartableTask(
- async ({ search, types, page, pageSize, useDebounce }) => {
+ async ({
+ search,
+ types,
+ page,
+ pageSize,
+ sortAttribute,
+ sortDirection,
+ useDebounce,
+ }) => {
if (useDebounce) {
await timeout(250);
}
@@ -61,6 +77,11 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route {
filters.type.push({ equals: type });
});
+ const sort = {
+ attribute: sortAttribute,
+ direction: sortDirection,
+ };
+
let credentialStores;
let totalItems = 0;
let doCredentialStoresExist = false;
@@ -71,7 +92,7 @@ export default class ScopesScopeCredentialStoresIndexRoute extends Route {
) {
credentialStores = await this.store.query('credential-store', {
scope_id,
- query: { search, filters },
+ query: { search, filters, sort },
page,
pageSize,
});
diff --git a/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs b/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs
index 6601411c2..29f174818 100644
--- a/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs
+++ b/ui/admin/app/templates/scopes/scope/credential-stores/index.hbs
@@ -56,49 +56,65 @@
{{#if @model.credentialStores}}
+ <:head as |H|>
+
+ {{t 'form.name.label'}}
+ {{t 'form.type.label'}}
+ {{t 'form.id.label'}}
+
+
<:body as |B|>
-
-
- {{#if (can 'read credential-store' B.data)}}
-
- {{B.data.displayName}}
-
- {{else}}
+ {{#each @model.credentialStores as |data|}}
+
+
+ {{#if (can 'read credential-store' data)}}
+
+ {{data.displayName}}
+
+ {{else}}
+
+ {{data.displayName}}
+
+ {{/if}}
- {{B.data.displayName}}
+ {{data.description}}
- {{/if}}
-
- {{B.data.description}}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {{/each}}
+
{
+ return this.server.create('credential-store', {
+ scope: instances.scopes.project,
+ type: TYPE_CREDENTIAL_STORE_STATIC,
+ created_time: `${year}-01-01T00:00:00Z`,
+ name: `Credential-store ${year}`,
+ });
+ });
+
+ await visit(urls.credentialStores);
+
+ assert.dom(commonSelectors.TABLE_ROWS).exists({ count: years.length });
+ years.forEach((year, index) => {
+ // nth-child index starts at 1
+ assert
+ .dom(commonSelectors.TABLE_ROW(index + 1))
+ .containsText(`Credential-store ${year}`);
+ });
+ });
+
+ test.each(
+ 'sorting',
+ {
+ 'on name': {
+ attribute: {
+ key: 'name',
+ values: ['Alpha', 'Beta', 'Delta', 'Gamma', 'Epsilon'],
+ },
+ expectedAscendingSort: ['Alpha', 'Beta', 'Delta', 'Epsilon', 'Gamma'],
+ column: 1,
+ },
+ 'on type': {
+ attribute: {
+ key: 'type',
+ values: ['Vault', 'Vault', 'Static', 'Vault', 'Static'],
+ },
+ expectedAscendingSort: ['Static', 'Static', 'Vault', 'Vault', 'Vault'],
+ column: 2,
+ },
+ 'on id': {
+ attribute: {
+ key: 'id',
+ values: ['cs_1000', 'cs_0001', 'cs_0100', 'cs_0010'],
+ },
+ expectedAscendingSort: ['cs_0001', 'cs_0010', 'cs_0100', 'cs_1000'],
+ column: 3,
+ },
+ },
+ async function (assert, input) {
+ this.server.schema.credentialStores.all().destroy();
+ faker.helpers.shuffle(input.attribute.values).forEach((value) => {
+ this.server.create('credential-store', {
+ [input.attribute.key]: value,
+ scope: instances.scopes.project,
+ });
+ });
+
+ await visit(urls.credentialStores);
+
+ // click the sort button again to sort in ascending order
+ await click(commonSelectors.TABLE_SORT_BTN(input.column));
+ assert.true(currentURL().includes('sortDirection=asc'));
+ assert.true(
+ currentURL().includes(`sortAttribute=${input.attribute.key}`),
+ );
+ assert
+ .dom(commonSelectors.TABLE_SORT_BTN_ARROW_UP(input.column))
+ .isVisible();
+
+ assert
+ .dom(commonSelectors.TABLE_ROWS)
+ .isVisible({ count: input.attribute.values.length });
+
+ input.expectedAscendingSort.forEach((expected, index) => {
+ // nth-child index starts at 1
+ assert.dom(commonSelectors.TABLE_ROW(index + 1)).containsText(expected);
+ });
+
+ // click the sort button again to sort in descending order
+ await click(commonSelectors.TABLE_SORT_BTN(input.column));
+ assert.true(currentURL().includes('sortDirection=desc'));
+ assert.true(
+ currentURL().includes(`sortAttribute=${input.attribute.key}`),
+ );
+ assert
+ .dom(commonSelectors.TABLE_SORT_BTN_ARROW_DOWN(input.column))
+ .isVisible();
+
+ input.expectedAscendingSort.toReversed().forEach((expected, index) => {
+ // nth-child index starts at 1
+ assert.dom(commonSelectors.TABLE_ROW(index + 1)).containsText(expected);
+ });
+ },
+ );
});