Skip to content

Commit

Permalink
fix: show tier-releated error msg for HVD clusters upon sync activati…
Browse files Browse the repository at this point in the history
…on (#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
  • Loading branch information
Noelle Daley authored May 24, 2024
1 parent e8c1da8 commit 3841f20
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 12 deletions.
9 changes: 3 additions & 6 deletions ui/lib/sync/addon/components/secrets/page/overview.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,9 @@
{{/if}}
{{/unless}}

{{#if this.activationError}}
{{#if this.activationErrors}}
{{#unless this.hideError}}
<MessageError
@errorMessage={{this.activationError}}
@onDismiss={{fn (mut this.hideError) true}}
data-test-opt-in-error
/>
<MessageError @errors={{this.activationErrors}} @onDismiss={{fn (mut this.hideError) true}} data-test-opt-in-error />
{{/unless}}
{{/if}}

Expand Down Expand Up @@ -204,6 +200,7 @@
<Secrets::SyncActivationModal
@onClose={{fn (mut this.showActivateSecretsSyncModal) false}}
@onError={{this.onModalError}}
@onConfirm={{this.clearActivationErrors}}
@isHvdManaged={{@isHvdManaged}}
/>
{{/if}}
19 changes: 17 additions & 2 deletions ui/lib/sync/addon/components/secrets/page/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,7 +35,7 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
@tracked destinationMetrics: SyncDestinationAssociationMetrics[] = [];
@tracked page = 1;
@tracked showActivateSecretsSyncModal = false;
@tracked activationError: null | string = null;
@tracked activationErrors: null | string[] = null;
// eventually remove when we deal with permissions on activation-features
@tracked hideOptIn = false;
@tracked hideError = false;
Expand All @@ -61,8 +62,22 @@ export default class SyncSecretsDestinationsPageComponent extends Component<Args
}
});

@action
clearActivationErrors() {
this.activationErrors = null;
}

@action
onModalError(errorMsg: string) {
this.activationError = errorMsg;
if (DEBUG) console.error(errorMsg); // eslint-disable-line no-console

const errors = [errorMsg];

if (this.args.isHvdManaged) {
errors.push(
'Secrets Sync is available for Plus tier clusters only. Please check the tier of your cluster to enable Secrets Sync.'
);
}
this.activationErrors = errors;
}
}
4 changes: 4 additions & 0 deletions ui/lib/sync/addon/components/secrets/sync-activation-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type RouterService from '@ember/routing/router-service';
interface Args {
onClose: () => void;
onError: (errorMessage: string) => void;
onConfirm: () => void;
isHvdManaged: boolean;
}

Expand All @@ -30,6 +31,9 @@ export default class SyncActivationModal extends Component<Args> {
@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;
Expand Down
72 changes: 69 additions & 3 deletions ui/tests/integration/components/sync/secrets/page/overview-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
<Secrets::SyncActivationModal @onClose={{this.onClose}} @onError={{this.onError}} @isHvdManaged={{this.isHvdManaged}}/>
<Secrets::SyncActivationModal @onClose={{this.onClose}} @onError={{this.onError}} @onConfirm={{this.onConfirm}} @isHvdManaged={{this.isHvdManaged}}/>
`,
{ owner: this.engine }
);
Expand Down Expand Up @@ -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', () => {
Expand Down

0 comments on commit 3841f20

Please sign in to comment.