diff --git a/changelog/_14197.txt b/changelog/_14197.txt
new file mode 100644
index 0000000000..b437f42dce
--- /dev/null
+++ b/changelog/_14197.txt
@@ -0,0 +1,3 @@
+```release-note:bug
+ui: Fix entities page to show success message after successfully editing an entity.
+```
diff --git a/ui/app/serializers/identity/_base.js b/ui/app/serializers/identity/_base.js
index 452cea5a16..4667d7305e 100644
--- a/ui/app/serializers/identity/_base.js
+++ b/ui/app/serializers/identity/_base.js
@@ -7,7 +7,10 @@ import ApplicationSerializer from '../application';
export default ApplicationSerializer.extend({
normalizeItems(payload) {
- if (payload.data.keys && Array.isArray(payload.data.keys)) {
+ // Extract the keys array into individual objects with the key as the id, if it exists.
+ // This is for endpoints that return a list of keys and a separate key_info object with
+ // the details for each key, such as the list endpoint for entities.
+ if (payload.data?.keys && Array.isArray(payload.data?.keys)) {
if (typeof payload.data.keys[0] !== 'string') {
// If keys is not an array of strings, it was already normalized into objects in extractLazyPaginatedData
return payload.data.keys;
diff --git a/ui/app/templates/vault/cluster/access/identity/index.hbs b/ui/app/templates/vault/cluster/access/identity/index.hbs
index 4fdc7b42cc..d21ed22b75 100644
--- a/ui/app/templates/vault/cluster/access/identity/index.hbs
+++ b/ui/app/templates/vault/cluster/access/identity/index.hbs
@@ -60,7 +60,11 @@
{{/if}}
{{/if}}
{{#if item.canEdit}}
-
Edit
+ Edit
{{#if item.disabled}}
Enable
{{else if (eq this.identityType "entity")}}
diff --git a/ui/tests/acceptance/access/identity/entities/index-test.js b/ui/tests/acceptance/access/identity/entities/index-test.js
index 7a03143dc0..233c8cba4b 100644
--- a/ui/tests/acceptance/access/identity/entities/index-test.js
+++ b/ui/tests/acceptance/access/identity/entities/index-test.js
@@ -136,4 +136,47 @@ module('Acceptance | /access/identity/entities', function (hooks) {
const message = `There was a problem deleting entity: test - ${error}`;
assert.true(flashSpy.calledWith(message), 'Correct flash message is shown');
});
+
+ test('it should render correct flash message on entity edit success', async function (assert) {
+ server.get('/identity/entity/id', () => ({
+ data: {
+ key_info: { test: { name: 'foo' } },
+ keys: ['test'],
+ },
+ }));
+ server.get('/identity/entity/id/test', () => ({ data: { name: 'foo' } }));
+
+ server.put('/identity/entity/id/test', () => new Response(200, {}, {}));
+
+ const flashSpy = sinon.spy(this.owner.lookup('service:flashMessages'), 'success');
+
+ await page.visit({ item_type: 'entities' });
+ await click(`${SELECTORS.listItem('foo')} ${GENERAL.menuTrigger}`);
+ await click(`${SELECTORS.listItem('foo')} ${GENERAL.menuItem('edit')}`);
+ await click(GENERAL.submitButton);
+
+ const message = `Successfully saved Entity test.`;
+ assert.true(flashSpy.calledWith(message), 'Correct flash message is shown');
+ });
+
+ test('it should render correct flash message on entity edit failure', async function (assert) {
+ server.get('/identity/entity/id', () => ({
+ data: {
+ key_info: { test: { name: 'foo' } },
+ keys: ['test'],
+ },
+ }));
+ server.get('/identity/entity/id/test', () => ({ data: { name: 'foo' } }));
+
+ const error = 'The entity could not be edited';
+ server.put('/identity/entity/id/test', () => new Response(500, {}, { errors: [error] }));
+
+ await page.visit({ item_type: 'entities' });
+ await click(`${SELECTORS.listItem('foo')} ${GENERAL.menuTrigger}`);
+ await click(`${SELECTORS.listItem('foo')} ${GENERAL.menuItem('edit')}`);
+ await click(GENERAL.submitButton);
+
+ assert.dom(GENERAL.messageError).exists();
+ assert.dom(GENERAL.messageDescription).hasText(error, 'Specific error message is rendered');
+ });
});