fix: 🐛 actions not displaying for templates in grants editor (#3296)

* 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
This commit is contained in:
Lisbet Alvarez authored and GitHub committed 2026-06-03 12:46:34 -07:00
1 parent 4a206c82dc
commit ecefa39b98
5 files changed
+76 -75

No files matched your search

@@ -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:
@@ -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]: {
+2 -8
View File
@@ -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 &&
@@ -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`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
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`
<GrantActions
@grantsSchema={{this.grantsSchema}}
@grantString={{this.grantString}}
/>
`);
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';
@@ -39,8 +39,7 @@ module(
[
{
selectedOption: 'resourceType',
expectedValue:
'type=<insert resource types>;actions=<insert actions>',
expectedValue: 'type=<insert resource type>;actions=<insert actions>',
},
{
optionToClick: RESOURCE_OPTION,
@@ -51,7 +50,7 @@ module(
optionToClick: PINNED_ID_OPTION,
selectedOption: 'pinnedId',
expectedValue:
'ids=<insert id>;type=<insert resource types>;actions=<insert actions>',
'ids=<insert ids>;type=<insert resource type>;actions=<insert actions>',
},
{
optionToClick: OUTPUT_FIELDS_OPTION,