From ecefa39b98301cdf8a028ae222e37f68ddeb81da Mon Sep 17 00:00:00 2001 From: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Wed, 3 Jun 2026 12:46:34 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20actions=20not=20displayin?= =?UTF-8?q?g=20for=20templates=20in=20grants=20editor=20(#3296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 🐛 actions not displaying for templates in grants editor * fix: 🐛 invalid ids check * fix: 🐛 update check for ids type * test: 💍 fix broken strings * test: 💍 fix test case * test: 💍 remove log --- addons/core/translations/resources/en-us.yaml | 4 +- .../components/grant-string-formats/index.js | 4 +- ui/admin/app/utils/grant-completions.js | 10 +- .../components/grant-actions/index-test.js | 128 ++++++++++-------- .../grant-string-formats/index-test.js | 5 +- 5 files changed, 76 insertions(+), 75 deletions(-) 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,