From 3841f204102f68134e9584e84bf415082058fa86 Mon Sep 17 00:00:00 2001 From: Noelle Daley Date: Thu, 23 May 2024 17:06:31 -0700 Subject: [PATCH] fix: show tier-releated error msg for HVD clusters upon sync activation (#27189) * fix: show tier-releated error msg for HVD clusters upon sync activation * fix: clear activation errors upon re-attempting to activate * tests: ensure only 1 error banner shows for non HVD clusters --- .../components/secrets/page/overview.hbs | 9 +-- .../addon/components/secrets/page/overview.ts | 19 ++++- .../secrets/sync-activation-modal.ts | 4 ++ .../sync/secrets/page/overview-test.js | 72 ++++++++++++++++++- .../secrets/sync-activation-modal-test.js | 12 +++- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/ui/lib/sync/addon/components/secrets/page/overview.hbs b/ui/lib/sync/addon/components/secrets/page/overview.hbs index 26e718616cfb..33fdea241802 100644 --- a/ui/lib/sync/addon/components/secrets/page/overview.hbs +++ b/ui/lib/sync/addon/components/secrets/page/overview.hbs @@ -34,13 +34,9 @@ {{/if}} {{/unless}} -{{#if this.activationError}} +{{#if this.activationErrors}} {{#unless this.hideError}} - + {{/unless}} {{/if}} @@ -204,6 +200,7 @@ {{/if}} \ No newline at end of file diff --git a/ui/lib/sync/addon/components/secrets/page/overview.ts b/ui/lib/sync/addon/components/secrets/page/overview.ts index eca498c30f1e..265d869b9306 100644 --- a/ui/lib/sync/addon/components/secrets/page/overview.ts +++ b/ui/lib/sync/addon/components/secrets/page/overview.ts @@ -9,6 +9,7 @@ import { service } from '@ember/service'; import { task } from 'ember-concurrency'; import { action } from '@ember/object'; import Ember from 'ember'; +import { DEBUG } from '@glimmer/env'; import type FlashMessageService from 'vault/services/flash-messages'; import type StoreService from 'vault/services/store'; @@ -34,7 +35,7 @@ export default class SyncSecretsDestinationsPageComponent extends Component void; onError: (errorMessage: string) => void; + onConfirm: () => void; isHvdManaged: boolean; } @@ -30,6 +31,9 @@ export default class SyncActivationModal extends Component { @task @waitFor *onFeatureConfirm() { + // clear any previous errors in the parent component + this.args.onConfirm(); + // must return null instead of root for non managed cluster. // child namespaces are not sent. const namespace = this.args.isHvdManaged ? 'admin' : null; diff --git a/ui/tests/integration/components/sync/secrets/page/overview-test.js b/ui/tests/integration/components/sync/secrets/page/overview-test.js index 1c7687cbc3d8..8684f662a6e3 100644 --- a/ui/tests/integration/components/sync/secrets/page/overview-test.js +++ b/ui/tests/integration/components/sync/secrets/page/overview-test.js @@ -8,10 +8,11 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { setupEngine } from 'ember-engines/test-support'; import { setupMirage } from 'ember-cli-mirage/test-support'; -import { render, click, settled } from '@ember/test-helpers'; +import { render, click, settled, findAll } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import syncScenario from 'vault/mirage/scenarios/sync'; import syncHandlers from 'vault/mirage/handlers/sync'; +import sinon from 'sinon'; import { PAGE } from 'vault/tests/helpers/sync/sync-selectors'; import { Response } from 'miragejs'; import { dateFormat } from 'core/helpers/date-format'; @@ -126,6 +127,34 @@ module('Integration | Component | sync | Page::Overview', function (hooks) { assert.dom(overview.optInBanner.container).doesNotExist('Opt-in banner is not shown'); }); + + test('it should show activation error if cluster is not Plus tier', async function (assert) { + await this.renderComponent(); + + this.server.post( + '/sys/activation-flags/secrets-sync/activate', + () => new Response(403, {}, { errors: ['Something bad happened'] }) + ); + + await click(overview.optInBanner.enable); + await click(overview.activationModal.checkbox); + await click(overview.activationModal.confirm); + + assert.dom(overview.optInError).exists({ count: 2 }, 'shows the API and custom tier error banners'); + + const errorBanners = findAll(overview.optInError); + + assert.dom(errorBanners[0]).containsText('Something bad happened', 'shows the API error message'); + + assert + .dom(errorBanners[1]) + .containsText( + 'Error Secrets Sync is available for Plus tier clusters only. Please check the tier of your cluster to enable Secrets Sync.', + 'shows the custom tier-related error message' + ); + + assert.dom(overview.optInBanner.container).exists('banner is visible so user can try to opt-in again'); + }); }); module('user does not have post permissions to activate', function (hooks) { @@ -186,15 +215,52 @@ module('Integration | Component | sync | Page::Overview', function (hooks) { test('it shows an error if activation fails', async function (assert) { await this.renderComponent(); - this.server.post('/sys/activation-flags/secrets-sync/activate', () => new Response(403)); + this.server.post( + '/sys/activation-flags/secrets-sync/activate', + () => new Response(403, {}, { errors: ['Something bad happened'] }) + ); await click(overview.optInBanner.enable); await click(overview.activationModal.checkbox); await click(overview.activationModal.confirm); - assert.dom(overview.optInError).exists('shows an error banner'); + assert + .dom(overview.optInError) + .exists({ count: 1 }) + .containsText('Something bad happened', 'shows an error banner with error message from the API'); assert.dom(overview.optInBanner.container).exists('banner is visible so user can try to opt-in again'); }); + + test('it should clear activation errors when the user tries to opt-in again', async function (assert) { + // don't worry about transitioning the route in this test + sinon.stub(this.owner.lookup('service:router'), 'refresh'); + + await this.renderComponent(); + + let callCount = 0; + + // first call fails, second call succeeds + this.server.post('/sys/activation-flags/secrets-sync/activate', () => { + callCount++; + if (callCount === 1) { + return new Response(403, {}, { errors: ['Something bad happened'] }); + } else { + return {}; + } + }); + + await click(overview.optInBanner.enable); + await click(overview.activationModal.checkbox); + await click(overview.activationModal.confirm); + + assert.dom(overview.optInError).exists('shows an error banner'); + + await click(overview.optInBanner.enable); + await click(overview.activationModal.checkbox); + await click(overview.activationModal.confirm); + + assert.dom(overview.optInError).doesNotExist('error banner is cleared upon trying to opt-in again'); + }); }); module('secrets sync is not activated and license does not have secrets sync', function (hooks) { diff --git a/ui/tests/integration/components/sync/secrets/sync-activation-modal-test.js b/ui/tests/integration/components/sync/secrets/sync-activation-modal-test.js index 21bc6fec510f..2de7b1813852 100644 --- a/ui/tests/integration/components/sync/secrets/sync-activation-modal-test.js +++ b/ui/tests/integration/components/sync/secrets/sync-activation-modal-test.js @@ -23,12 +23,13 @@ module('Integration | Component | Secrets::SyncActivationModal', function (hooks hooks.beforeEach(function () { this.onClose = sinon.stub(); this.onError = sinon.stub(); + this.onConfirm = sinon.stub(); this.isHvdManaged = false; this.renderComponent = async () => { await render( hbs` - + `, { owner: this.engine } ); @@ -70,6 +71,15 @@ module('Integration | Component | Secrets::SyncActivationModal', function (hooks this.refreshStub = sinon.stub(router, 'refresh'); }); + test('it calls onConfirm', async function (assert) { + await this.renderComponent(); + + await click(SELECTORS.checkbox); + await click(SELECTORS.confirm); + + assert.true(this.onConfirm.called); + }); + module('success', function (hooks) { hooks.beforeEach(function () { this.server.post('/sys/activation-flags/secrets-sync/activate', () => {