From 63ec418cf0f004636acfc9683e1f1b2092ad4b0e Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Wed, 14 Aug 2024 14:32:56 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Show=20cache=20daemon=20?= =?UTF-8?q?errors=20from=20status=20(#2437)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14816 --- addons/core/translations/errors/en-us.yaml | 2 +- .../settings-card/application/index.hbs | 17 +++- .../scopes/scope/projects/settings/index.js | 23 ++++- .../src/utils/generateErrorPromise.js | 2 +- .../settings-card/application-test.js | 91 +++++++++++++++++++ 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 ui/desktop/tests/integration/components/settings-card/application-test.js diff --git a/addons/core/translations/errors/en-us.yaml b/addons/core/translations/errors/en-us.yaml index d8893de1a..7c5207a33 100644 --- a/addons/core/translations/errors/en-us.yaml +++ b/addons/core/translations/errors/en-us.yaml @@ -28,4 +28,4 @@ required-field: This is a required field. client-agent-failed: sessions: Unable to retrieve sessions from the client agent. settings-cache: - title: There is a problem with the cache daemon + title: There may be a problem with the cache daemon diff --git a/ui/desktop/app/components/settings-card/application/index.hbs b/ui/desktop/app/components/settings-card/application/index.hbs index fa20d0076..1e91984fe 100644 --- a/ui/desktop/app/components/settings-card/application/index.hbs +++ b/ui/desktop/app/components/settings-card/application/index.hbs @@ -25,7 +25,7 @@ {{t 'settings.cache-daemon'}} - + {{#if @model.formattedCacheVersion}} {{@model.formattedCacheVersion}} - {{#if @model.error}} + {{#if @model.cacheDaemonErrors}} {{t 'errors.settings-cache.title'}} - {{#if @model.error.message}} - {{@model.error.message}} - {{/if}} + + {{#each @model.cacheDaemonErrors as |error|}} + + {{#if error.name}} + {{error.name}}: + {{/if}} + {{error.message}} + + {{/each}} + {{/if}} diff --git a/ui/desktop/app/routes/scopes/scope/projects/settings/index.js b/ui/desktop/app/routes/scopes/scope/projects/settings/index.js index 1d0fc1c47..82c26d5dd 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/settings/index.js +++ b/ui/desktop/app/routes/scopes/scope/projects/settings/index.js @@ -7,14 +7,17 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class ScopesScopeProjectsSettingsIndexRoute extends Route { + // =services @service ipc; + @service session; + // =methods async model() { let cacheDaemonStatus, formattedCacheVersion, formattedCliVersion, formattedDesktopVersion, - error; + cacheDaemonErrors = []; const { versionNumber: cliVersion } = await this.ipc.invoke('getCliVersion'); @@ -26,16 +29,28 @@ export default class ScopesScopeProjectsSettingsIndexRoute extends Route { try { cacheDaemonStatus = await this.ipc.invoke('cacheDaemonStatus'); } catch (e) { - error = e; + cacheDaemonErrors.push(e); } - const { version } = cacheDaemonStatus ?? {}; + const { version, users } = cacheDaemonStatus ?? {}; formattedCacheVersion = version?.match(/v\d+\.\d+\.\d+/)?.[0]; + // Grab the errors from each resource + const userId = this.session.data.authenticated.user_id; + const resourceErrors = + users + ?.find((user) => user.id === userId) + ?.resources.filter((resource) => resource?.last_error?.error) + ?.map((resource) => ({ + message: resource.last_error.error, + name: resource.name, + })) ?? []; + cacheDaemonErrors = [...cacheDaemonErrors, ...resourceErrors]; + const logLevel = await this.ipc.invoke('getLogLevel'); const logPath = await this.ipc.invoke('getLogPath'); return { - error, + cacheDaemonErrors, formattedCliVersion, formattedCacheVersion, formattedDesktopVersion, diff --git a/ui/desktop/electron-app/src/utils/generateErrorPromise.js b/ui/desktop/electron-app/src/utils/generateErrorPromise.js index eb78864ad..341d60e4d 100644 --- a/ui/desktop/electron-app/src/utils/generateErrorPromise.js +++ b/ui/desktop/electron-app/src/utils/generateErrorPromise.js @@ -12,7 +12,7 @@ const generateErrorPromise = async (stderr) => { if (parsedResponse?.status_code) { return Promise.reject({ statusCode: parsedResponse?.status_code, - message: parsedResponse?.api_error, + ...parsedResponse?.api_error, }); } diff --git a/ui/desktop/tests/integration/components/settings-card/application-test.js b/ui/desktop/tests/integration/components/settings-card/application-test.js new file mode 100644 index 000000000..afed0dc8f --- /dev/null +++ b/ui/desktop/tests/integration/components/settings-card/application-test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import { setupIntl } from 'ember-intl/test-support'; + +const CACHE_DAEMON_VERSION = '[data-test-cache-version]'; +const ERROR_MESSAGES = '.hds-alert__description > p'; +const FIRST_ERROR_MESSAGE = '.hds-alert__description > p:first-child'; +const LAST_ERROR_MESSAGE = '.hds-alert__description > p:last-child'; + +module('Integration | Component | settings-card/application', function (hooks) { + setupRenderingTest(hooks); + setupIntl(hooks); + + test('it renders', async function (assert) { + this.set('model', { + cacheDaemonErrors: [], + formattedDesktopVersion: 'v2.0.0', + formattedCliVersion: 'v3.0.0', + formattedCacheVersion: 'v4.0.0', + }); + this.set('toggleTheme', () => {}); + + await render( + hbs``, + ); + + assert.dom(this.element).includesText('v2.0.0'); + assert.dom(this.element).includesText('v3.0.0'); + assert.dom(this.element).includesText('v4.0.0'); + }); + + test('it renders errors', async function (assert) { + this.set('model', { + cacheDaemonErrors: [ + { + message: `this is a resolvable alias error`, + name: 'resolvable-alias', + }, + { + message: `this is a target error`, + name: 'target', + }, + ], + formattedDesktopVersion: 'v2.0.0', + formattedCliVersion: 'v3.0.0', + formattedCacheVersion: 'v4.0.0', + }); + this.set('toggleTheme', () => {}); + + await render( + hbs``, + ); + + assert.dom(ERROR_MESSAGES).isVisible({ count: 2 }); + assert + .dom(FIRST_ERROR_MESSAGE) + .hasText('resolvable-alias: this is a resolvable alias error'); + assert.dom(LAST_ERROR_MESSAGE).hasText('target: this is a target error'); + }); + + test('it indicates when cache daemon is running', async function (assert) { + this.set('model', { + cacheDaemonErrors: [], + formattedDesktopVersion: 'v2.0.0', + formattedCliVersion: 'v3.0.0', + formattedCacheVersion: 'v4.0.0', + }); + this.set('toggleTheme', () => {}); + + await render( + hbs``, + ); + + assert.dom(CACHE_DAEMON_VERSION).hasText('v4.0.0 Running'); + + this.set('model', { + cacheDaemonErrors: [], + formattedDesktopVersion: 'v2.0.0', + formattedCliVersion: 'v3.0.0', + formattedCacheVersion: '', + }); + assert.dom(CACHE_DAEMON_VERSION).hasText('Not Running'); + }); +});