diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml
index cd2ccab7a..ae8278633 100644
--- a/addons/core/translations/resources/en-us.yaml
+++ b/addons/core/translations/resources/en-us.yaml
@@ -758,8 +758,8 @@ role:
label: User template
help: Substitutes the user ID from the token used to perform the request.
insert-actions: insert actions
- insert-resource-types: insert resource types
- insert-id: insert id
+ insert-resource-type: insert resource type
+ insert-ids: insert ids
insert-resource-ids: insert resource ids
insert-output-fields: insert output fields
export:
diff --git a/ui/admin/app/components/grant-string-formats/index.js b/ui/admin/app/components/grant-string-formats/index.js
index 64c2b41b3..65f1a813c 100644
--- a/ui/admin/app/components/grant-string-formats/index.js
+++ b/ui/admin/app/components/grant-string-formats/index.js
@@ -28,7 +28,7 @@ export default class GrantStringFormatsIndex extends Component {
stringFormats = {
[this.stringFormatTypes.resourceType]: {
- text: `type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-types')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
+ text: `type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-type')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
link: 'role.grant-string-format.resource-type',
},
[this.stringFormatTypes.resource]: {
@@ -36,7 +36,7 @@ export default class GrantStringFormatsIndex extends Component {
link: 'role.grant-string-format.resource',
},
[this.stringFormatTypes.pinnedId]: {
- text: `ids=<${this.intl.t('resources.role.edit-grants.string-formats.insert-id')}>;type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-types')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
+ text: `ids=<${this.intl.t('resources.role.edit-grants.string-formats.insert-ids')}>;type=<${this.intl.t('resources.role.edit-grants.string-formats.insert-resource-type')}>;actions=<${this.intl.t('resources.role.edit-grants.string-formats.insert-actions')}>`,
link: 'role.grant-string-format.pinned-id',
},
[this.stringFormatTypes.outputFields]: {
diff --git a/ui/admin/app/utils/grant-completions.js b/ui/admin/app/utils/grant-completions.js
index 5e45c263a..80e1fc412 100644
--- a/ui/admin/app/utils/grant-completions.js
+++ b/ui/admin/app/utils/grant-completions.js
@@ -101,18 +101,12 @@ export const analyzeGrantString = (grantsSchema, grantString = '') => {
const hasSpecificIds = Boolean(idsValue) && !idsValue.includes('*');
const hasExplicitType = Boolean(typeValue) && typeValue !== '*';
- const hasTemplateIds =
- hasSpecificIds &&
- parseIds(idsValue).some((id) => id.startsWith('{{') && id.endsWith('}}'));
-
- const hasLiteralIds = hasSpecificIds && !hasTemplateIds;
-
- const compatibleIdsResourceType = hasLiteralIds
+ const compatibleIdsResourceType = hasSpecificIds
? getCompatibleResourceTypeForIds(schema, idsValue)
: null;
const hasInvalidType = hasExplicitType && !schema.resourcesByType[typeValue];
- const hasInvalidIds = hasLiteralIds && !compatibleIdsResourceType;
+ const hasInvalidIds = hasSpecificIds && !compatibleIdsResourceType;
const hasInvalidPinnedIdTypeCombination =
hasSpecificIds &&
diff --git a/ui/admin/tests/integration/components/grant-actions/index-test.js b/ui/admin/tests/integration/components/grant-actions/index-test.js
index cac188f37..c8e66d8b1 100644
--- a/ui/admin/tests/integration/components/grant-actions/index-test.js
+++ b/ui/admin/tests/integration/components/grant-actions/index-test.js
@@ -6,6 +6,7 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'admin/tests/helpers';
import { setupIntl } from 'ember-intl/test-support';
+import { setupMirage } from 'admin/tests/helpers/mirage';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import * as commonSelectors from 'admin/tests/helpers/selectors';
@@ -19,40 +20,18 @@ const GRANT_ACTIONS_INVALID_ID_OR_TYPE =
module('Integration | Component | grant-actions/index', function (hooks) {
setupRenderingTest(hooks);
+ setupMirage(hooks);
setupIntl(hooks, 'en-us');
- hooks.beforeEach(function () {
- this.grantsSchema = {
- resource_types: [
- {
- type: 'host-catalog',
- collection_actions: ['create', 'list'],
- id_actions: ['read', 'update', 'delete'],
- id_prefixes: ['hcst'],
- },
- {
- type: 'host',
- parent_type: 'host-catalog',
- collection_actions: ['create', 'list'],
- id_actions: ['read', 'update', 'delete'],
- id_prefixes: ['h'],
- },
- {
- type: 'host-set',
- parent_type: 'host-catalog',
- collection_actions: ['create', 'list'],
- id_actions: [
- 'delete',
- 'add-hosts',
- 'set-hosts',
- 'remove-hosts',
- 'read',
- 'update',
- ],
- id_prefixes: ['hs'],
- },
- ],
- };
+ hooks.beforeEach(async function () {
+ const response = await fetch('/grants-schema.json');
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch grants schema: ${response.status}`);
+ }
+
+ const grantsSchemaData = await response.json();
+ this.grantsSchema = grantsSchemaData;
});
test('it renders the actions relevant to the current grant line', async function (assert) {
@@ -68,16 +47,13 @@ module('Integration | Component | grant-actions/index', function (hooks) {
assert.dom(GRANT_ACTIONS_TABLE).isVisible();
assert.dom(commonSelectors.TABLE_ROWS).isVisible({ count: 8 });
- const rows = [...this.element.querySelectorAll(commonSelectors.TABLE_ROWS)];
- const rowData = rows.map((row) => {
- const cells = [...row.querySelectorAll('td')].map((cell) =>
+ const rowData = [
+ ...this.element.querySelectorAll(commonSelectors.TABLE_ROWS),
+ ].map((row) => {
+ const [name, description] = [...row.querySelectorAll('td')].map((cell) =>
cell.textContent.trim(),
);
-
- return {
- name: cells[0],
- description: cells[1],
- };
+ return { name, description };
});
assert.deepEqual(
@@ -98,12 +74,10 @@ module('Integration | Component | grant-actions/index', function (hooks) {
name: 'add-hosts',
description: 'Add hosts to a host set',
});
-
assert.deepEqual(rowData[1], {
name: 'create',
description: 'Create a new host-set',
});
-
assert.deepEqual(rowData.at(-1), {
name: 'update',
description: 'Update a host-set',
@@ -113,6 +87,58 @@ module('Integration | Component | grant-actions/index', function (hooks) {
assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
});
+ test('it renders relevant actions for template IDs', async function (assert) {
+ this.grantString = 'ids={{.User.Id}}';
+
+ await render(hbs`
+
+ `);
+
+ assert.dom(GRANT_ACTIONS_TABLE).isVisible();
+ assert.dom(commonSelectors.TABLE_ROWS).isVisible({ count: 7 });
+
+ const rowData = [
+ ...this.element.querySelectorAll(commonSelectors.TABLE_ROWS),
+ ].map((row) => {
+ const [name, description] = [...row.querySelectorAll('td')].map((cell) =>
+ cell.textContent.trim(),
+ );
+ return { name, description };
+ });
+
+ assert.deepEqual(
+ rowData.map(({ name }) => name),
+ [
+ 'add-accounts',
+ 'delete',
+ 'list-resolvable-aliases',
+ 'read',
+ 'remove-accounts',
+ 'set-accounts',
+ 'update',
+ ],
+ );
+
+ assert.deepEqual(rowData[0], {
+ name: 'add-accounts',
+ description: 'Add accounts to a user',
+ });
+ assert.deepEqual(rowData[1], {
+ name: 'delete',
+ description: 'Delete a user',
+ });
+ assert.deepEqual(rowData.at(-1), {
+ name: 'update',
+ description: 'Update a user',
+ });
+
+ assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
+ assert.dom(GRANT_ACTIONS_NO_TYPE_DETECTED).doesNotExist();
+ });
+
test('it renders the no resource type detected state when the current line has no detectable type', async function (assert) {
this.grantString = 'output_fields=*';
@@ -201,24 +227,6 @@ module('Integration | Component | grant-actions/index', function (hooks) {
assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
});
- test('it renders the no-type-detected state for template IDs', async function (assert) {
- this.grantString = 'ids={{.User.Id}}';
-
- await render(hbs`
-
- `);
-
- assert.dom(GRANT_ACTIONS_TABLE).doesNotExist();
- assert.dom(GRANT_ACTIONS_INVALID_ID_OR_TYPE).doesNotExist();
- assert
- .dom(GRANT_ACTIONS_NO_TYPE_DETECTED)
- .hasText('No resource type detected.');
- assert.dom(GRANT_ACTIONS_EMPTY_STATE).doesNotExist();
- });
-
test('it renders the invalid state for a pinned id paired with its own type instead of a child type', async function (assert) {
this.grantString = 'ids=hcst_1234567890;type=host-catalog';
diff --git a/ui/admin/tests/integration/components/grant-string-formats/index-test.js b/ui/admin/tests/integration/components/grant-string-formats/index-test.js
index 9561196aa..0869ee39c 100644
--- a/ui/admin/tests/integration/components/grant-string-formats/index-test.js
+++ b/ui/admin/tests/integration/components/grant-string-formats/index-test.js
@@ -39,8 +39,7 @@ module(
[
{
selectedOption: 'resourceType',
- expectedValue:
- 'type=;actions=',
+ expectedValue: 'type=;actions=',
},
{
optionToClick: RESOURCE_OPTION,
@@ -51,7 +50,7 @@ module(
optionToClick: PINNED_ID_OPTION,
selectedOption: 'pinnedId',
expectedValue:
- 'ids=;type=;actions=',
+ 'ids=;type=;actions=',
},
{
optionToClick: OUTPUT_FIELDS_OPTION,