From c49a9970573a98b25bd86a53f3d9445a128edf96 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:17:22 -0700 Subject: [PATCH] Switch from toast to callout for integration set up failures (#1155) (#1158) (cherry picked from commit 095bdfa1b3a670acd6609019babfc431a330bb8e) Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] (cherry picked from commit e090a8860a034ef4a548be6c61ae41dc62a583f2) --- auto_sync_commit_metadata.json | 4 +- .../setup_integration.test.tsx.snap | 122 +++++++++--------- .../__tests__/setup_integration.test.tsx | 1 + .../available_integration_overview_page.tsx | 18 ++- .../components/create_integration_helpers.ts | 13 +- .../components/setup_integration.tsx | 51 ++++++-- .../repository/fs_data_adaptor.ts | 15 ++- 7 files changed, 137 insertions(+), 87 deletions(-) diff --git a/auto_sync_commit_metadata.json b/auto_sync_commit_metadata.json index 4b29887dc..a85feadff 100644 --- a/auto_sync_commit_metadata.json +++ b/auto_sync_commit_metadata.json @@ -1,4 +1,4 @@ { - "last_github_commit": "2ac5d15fc3b1808290313a812e841e035ea0cc64", - "last_gitfarm_commit": "3565a03664516805ff12c1baa3c51e0473d38a29" + "last_github_commit": "e090a8860a034ef4a548be6c61ae41dc62a583f2", + "last_gitfarm_commit": "06aefad40784add9c3197ce60b7c3b39acc6ffba" } \ No newline at end of file diff --git a/public/components/integrations/components/__tests__/__snapshots__/setup_integration.test.tsx.snap b/public/components/integrations/components/__tests__/__snapshots__/setup_integration.test.tsx.snap index 3bc959196..a36eb7635 100644 --- a/public/components/integrations/components/__tests__/__snapshots__/setup_integration.test.tsx.snap +++ b/public/components/integrations/components/__tests__/__snapshots__/setup_integration.test.tsx.snap @@ -42,6 +42,11 @@ exports[`Integration Setup Page Renders integration setup page as expected 1`] = "type": "", } } + setupCallout={ + Object { + "show": false, + } + } updateConfig={[Function]} > @@ -60,6 +65,11 @@ exports[`Integration Setup Page Renders integration setup page as expected 1`] = className="euiSpacer euiSpacer--l" /> + +
+
@@ -707,11 +718,11 @@ exports[`Integration Setup Page Renders integration setup page as expected 1`] = class="euiFlexItem euiFlexItem--flexGrowZero" > - - + + + +
@@ -1047,6 +1048,11 @@ exports[`Integration Setup Page Renders the form as expected 1`] = ` className="euiSpacer euiSpacer--l" /> + +
+
{ config={TEST_INTEGRATION_SETUP_INPUTS} updateConfig={() => {}} integration={TEST_INTEGRATION_CONFIG} + setupCallout={{ show: false }} /> ); diff --git a/public/components/integrations/components/available_integration_overview_page.tsx b/public/components/integrations/components/available_integration_overview_page.tsx index 360bf0db4..cd9a8300b 100644 --- a/public/components/integrations/components/available_integration_overview_page.tsx +++ b/public/components/integrations/components/available_integration_overview_page.tsx @@ -32,9 +32,9 @@ export interface AvailableIntegrationType { version?: string | undefined; displayName?: string; integrationType: string; - statics: any; - components: any[]; - displayAssets: any[]; + statics: unknown; + components: Array<{ name: string }>; + displayAssets: unknown[]; } export interface AvailableIntegrationsTableProps { @@ -183,7 +183,11 @@ export function AvailableIntegrationOverviewPage(props: AvailableIntegrationOver ? AvailableIntegrationsCardView({ data: { hits: data.hits.filter((hit) => - helper.every((compon) => hit.components.map((x) => x.name).includes(compon)) + helper.every((compon) => + hit.components + .map((x) => x.name.split('_').findLast(() => true)) + .includes(compon) + ) ), }, isCardView, @@ -197,7 +201,11 @@ export function AvailableIntegrationOverviewPage(props: AvailableIntegrationOver loading: false, data: { hits: data.hits.filter((hit) => - helper.every((compon) => hit.components.map((x) => x.name).includes(compon)) + helper.every((compon) => + hit.components + .map((x) => x.name.split('_').findLast(() => true)) + .includes(compon) + ) ), }, isCardView, diff --git a/public/components/integrations/components/create_integration_helpers.ts b/public/components/integrations/components/create_integration_helpers.ts index 7e465c26b..1bebd558f 100644 --- a/public/components/integrations/components/create_integration_helpers.ts +++ b/public/components/integrations/components/create_integration_helpers.ts @@ -313,16 +313,13 @@ export async function addIntegrationRequest( .post(`${INTEGRATIONS_BASE}/store/${templateName}`, { body: JSON.stringify({ name, dataSource }), }) - .then((_res) => { + .then((res) => { setToast(`${name} integration successfully added!`, 'success'); - window.location.hash = `#/installed/${_res.data?.id}`; + window.location.hash = `#/installed/${res.data?.id}`; return true; }) - .catch((_err) => { - setToast( - 'Failed to load integration. Check Added Integrations table for more details', - 'danger' - ); + .catch((err) => { + setToast('Failed to load integration', 'danger', err.message); return false; }); if (!addSample || !response) { @@ -333,7 +330,7 @@ export async function addIntegrationRequest( .then((res) => res.data) .catch((err) => { console.error(err); - setToast('The sample data could not be retrieved', 'danger'); + setToast('Failed to load integration', 'danger', 'The sample data could not be retrieved.'); return { sampleData: [] }; }); const requestBody = diff --git a/public/components/integrations/components/setup_integration.tsx b/public/components/integrations/components/setup_integration.tsx index 6af95a224..37884b4f0 100644 --- a/public/components/integrations/components/setup_integration.tsx +++ b/public/components/integrations/components/setup_integration.tsx @@ -6,6 +6,8 @@ import { EuiBottomBar, EuiButton, + EuiButtonEmpty, + EuiCallOut, EuiComboBox, EuiFieldText, EuiFlexGroup, @@ -24,9 +26,9 @@ import { EuiTitle, } from '@elastic/eui'; import React, { useState, useEffect } from 'react'; +import { Color } from 'common/constants/integrations'; import { coreRefs } from '../../../framework/core_refs'; import { IntegrationTemplate, addIntegrationRequest } from './create_integration_helpers'; -import { useToast } from '../../../../public/components/common/toast'; import { CONSOLE_PROXY, INTEGRATIONS_BASE } from '../../../../common/constants/shared'; import { DATACONNECTIONS_BASE } from '../../../../common/constants/shared'; @@ -37,10 +39,13 @@ export interface IntegrationSetupInputs { connectionLocation: string; } +type SetupCallout = { show: true; title: string; color?: Color; text?: string } | { show: false }; + interface IntegrationConfigProps { config: IntegrationSetupInputs; updateConfig: (updates: Partial) => void; integration: IntegrationTemplate; + setupCallout: SetupCallout; } // TODO support localization @@ -155,7 +160,10 @@ const runQuery = async ( trackProgress(3); return { ok: true, value: poll }; } else if (poll.status === 'FAILURE') { - return { ok: false, error: new Error('FAILURE status', { cause: poll }) }; + return { + ok: false, + error: new Error(poll.error ?? 'No error information provided', { cause: poll }), + }; } await sleep(3000); } @@ -169,6 +177,7 @@ export function SetupIntegrationForm({ config, updateConfig, integration, + setupCallout, }: IntegrationConfigProps) { const connectionType = INTEGRATION_CONNECTION_DATA_SOURCE_TYPES.get(config.connectionType)!; @@ -193,6 +202,12 @@ export function SetupIntegrationForm({

Set Up Integration

+ {setupCallout.show ? ( + +

{setupCallout.text}

+
+ ) : null} +

Integration Details

@@ -261,6 +276,7 @@ export function SetupBottomBar({ setLoading, loadingProgress, setProgress, + setSetupCallout, }: { config: IntegrationSetupInputs; integration: IntegrationTemplate; @@ -268,15 +284,23 @@ export function SetupBottomBar({ setLoading: (loading: boolean) => void; loadingProgress: number; setProgress: (updater: number | ((progress: number) => number)) => void; + setSetupCallout: (setupCallout: SetupCallout) => void; }) { - const { setToast } = useToast(); + // Drop-in replacement for setToast + const setCalloutLikeToast = (title: string, color?: Color, text?: string) => + setSetupCallout({ + show: true, + title, + color, + text, + }); return ( - { // TODO evil hack because props aren't set up @@ -287,7 +311,7 @@ export function SetupBottomBar({ }} > Discard - + )} @@ -441,6 +471,7 @@ export function SetupIntegrationPage({ integration }: { integration: string }) { setLoading={setShowLoading} loadingProgress={loadingProgress} setProgress={setLoadingProgress} + setSetupCallout={setSetupCallout} /> diff --git a/server/adaptors/integrations/repository/fs_data_adaptor.ts b/server/adaptors/integrations/repository/fs_data_adaptor.ts index df1c6781a..b6915d5ee 100644 --- a/server/adaptors/integrations/repository/fs_data_adaptor.ts +++ b/server/adaptors/integrations/repository/fs_data_adaptor.ts @@ -48,6 +48,15 @@ function tryParseNDJson(content: string): object[] | null { } } +// Check if a location is a directory without an exception if location not found +const safeIsDirectory = async (maybeDirectory: string): Promise => { + try { + return (await fs.lstat(maybeDirectory)).isDirectory(); + } catch (_err: unknown) { + return false; + } +}; + /** * A CatalogDataAdaptor that reads from the local filesystem. * Used to read Integration information when the user uploads their own catalog. @@ -133,15 +142,13 @@ export class FileSystemCatalogDataAdaptor implements CatalogDataAdaptor { } async getDirectoryType(dirname?: string): Promise<'integration' | 'repository' | 'unknown'> { - const isDir = (await fs.lstat(path.join(this.directory, dirname ?? '.'))).isDirectory(); + const isDir = await safeIsDirectory(path.join(this.directory, dirname ?? '.')); if (!isDir) { return 'unknown'; } // Sloppily just check for one mandatory integration directory to distinguish. // Improve if we need to distinguish a repository with an integration named "schemas". - const hasSchemas = ( - await fs.lstat(path.join(this.directory, dirname ?? '.', 'schemas')) - ).isDirectory(); + const hasSchemas = await safeIsDirectory(path.join(this.directory, dirname ?? '.', 'schemas')); return hasSchemas ? 'integration' : 'repository'; }