Skip to content

Commit

Permalink
Added drill-down details for workflow-created resources (#462) (#467)
Browse files Browse the repository at this point in the history
* Added drill-down details for workflow-created resources



* Added drill-down details for workflow-created resources



* Added drill-down details for workflow-created resources



* Added drill-down details for workflow-created resources



* Moved fetchResourceData in public/utils



* Added drill-down details for workflow-created resources



* Added drill-down details for workflow-created resources



* Fixing unit tests



* Fixing error message



* defined getSearchPipeline path in clusterserver/cluster



* small fix



* Added drill-down details for workflow-created resources



* Added drill-down details for workflow-created resources



* Removed callback



---------


(cherry picked from commit 809c006)

Signed-off-by: saimedhi <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 48f3ff7 commit 9c5026e
Show file tree
Hide file tree
Showing 16 changed files with 954 additions and 67 deletions.
8 changes: 8 additions & 0 deletions common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export const ML_CONNECTOR_ROUTE_PREFIX = `${ML_API_ROUTE_PREFIX}/connectors`;
export const ML_SEARCH_MODELS_ROUTE = `${ML_MODEL_ROUTE_PREFIX}/_search`;
export const ML_SEARCH_CONNECTORS_ROUTE = `${ML_CONNECTOR_ROUTE_PREFIX}/_search`;

/**
* OpenSearch APIs
*/
export const SEARCH_PIPELINE_ROUTE = '/_search/pipeline';

/**
* NODE APIs
*/
Expand All @@ -40,10 +45,13 @@ export const BASE_NODE_API_PATH = '/api/flow_framework';
export const BASE_OPENSEARCH_NODE_API_PATH = `${BASE_NODE_API_PATH}/opensearch`;
export const CAT_INDICES_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/catIndices`;
export const GET_MAPPINGS_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/mappings`;
export const GET_INDEX_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/getIndex`;
export const SEARCH_INDEX_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/search`;
export const INGEST_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/ingest`;
export const BULK_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/bulk`;
export const SIMULATE_PIPELINE_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/simulatePipeline`;
export const INGEST_PIPELINE_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/getIngestPipeline`;
export const SEARCH_PIPELINE_NODE_API_PATH = `${BASE_OPENSEARCH_NODE_API_PATH}/getSearchPipeline`;

// Flow Framework node APIs
export const BASE_WORKFLOW_NODE_API_PATH = `${BASE_NODE_API_PATH}/workflow`;
Expand Down
15 changes: 15 additions & 0 deletions common/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,18 @@ export type SimulateIngestPipelineResponse = {
};

export type SearchHit = SimulateIngestPipelineDoc;

export type IndexResponse = {
indexName: string;
indexDetails: IndexConfiguration;
};

export type IngestPipelineResponse = {
pipelineId: string;
ingestPipelineDetails: IngestPipelineConfig;
};

export type SearchPipelineResponse = {
pipelineId: string;
searchPipelineDetails: SearchPipelineConfig;
};
1 change: 0 additions & 1 deletion public/general_components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@

export { MultiSelectFilter } from './multi_select_filter';
export { ProcessorsTitle } from './processors_title';
export { ResourceList } from './resource_list';
60 changes: 0 additions & 60 deletions public/general_components/resource_list.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState, useEffect } from 'react';
import {
Direction,
EuiCodeBlock,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiInMemoryTable,
EuiTitle,
EuiIcon,
EuiText,
EuiEmptyPrompt,
EuiLoadingSpinner,
} from '@elastic/eui';
import {
Workflow,
WorkflowResource,
customStringify,
} from '../../../../../common';
import {
AppState,
useAppDispatch,
getIndex,
getIngestPipeline,
getSearchPipeline,
} from '../../../../store';
import {
extractIdsByStepType,
getDataSourceId,
getErrorMessageForStepType,
} from '../../../../utils';
import { columns } from './columns';
import { useSelector } from 'react-redux';

interface ResourceListFlyoutProps {
workflow?: Workflow;
}

/**
* The searchable list of resources for a particular workflow.
*/
export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
const [allResources, setAllResources] = useState<WorkflowResource[]>([]);
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();
const [resourceDetails, setResourceDetails] = useState<string | null>(null);
const [rowErrorMessage, setRowErrorMessage] = useState<string | null>(null);
const {
loading,
getIndexErrorMessage,
getIngestPipelineErrorMessage,
getSearchPipelineErrorMessage,
indexDetails,
ingestPipelineDetails,
searchPipelineDetails,
} = useSelector((state: AppState) => state.opensearch);

// Hook to initialize all resources. Reduce to unique IDs, since
// the backend resources may include the same resource multiple times
// (e.g., register and deploy steps persist the same model ID resource)
useEffect(() => {
if (props.workflow?.resourcesCreated) {
const resourcesMap = {} as { [id: string]: WorkflowResource };
props.workflow.resourcesCreated.forEach((resource) => {
resourcesMap[resource.id] = resource;
});
setAllResources(Object.values(resourcesMap));
}
}, [props.workflow?.resourcesCreated]);

useEffect(() => {
const {
indexIds,
ingestPipelineIds,
searchPipelineIds,
} = extractIdsByStepType(allResources);

if (indexIds) {
try {
dispatch(getIndex({ index: indexIds, dataSourceId }));
} catch {}
}

if (ingestPipelineIds) {
try {
dispatch(
getIngestPipeline({ pipelineId: ingestPipelineIds, dataSourceId })
);
} catch {}
}

if (searchPipelineIds) {
try {
dispatch(
getSearchPipeline({ pipelineId: searchPipelineIds, dataSourceId })
);
} catch {}
}
}, [allResources]);

const sorting = {
sort: {
field: 'id',
direction: 'asc' as Direction,
},
};

const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
const [selectedRowData, setSelectedRowData] = useState<
WorkflowResource | undefined
>(undefined);

// Opens the flyout and fetches resource details for the selected row.
const openFlyout = async (row: WorkflowResource) => {
setSelectedRowData(row);
setIsFlyoutVisible(true);
const value =
indexDetails[row.id] ??
ingestPipelineDetails[row.id] ??
searchPipelineDetails[row.id] ??
'';
setResourceDetails(customStringify({ [row.id]: value }));
const resourceDetailsErrorMessage = getErrorMessageForStepType(
row.stepType,
getIndexErrorMessage,
getIngestPipelineErrorMessage,
getSearchPipelineErrorMessage
);
setRowErrorMessage(resourceDetailsErrorMessage);
};

// Closes the flyout and resets the selected resource data.
const closeFlyout = () => {
setIsFlyoutVisible(false);
setSelectedRowData(undefined);
setResourceDetails(null);
};

return (
<>
<EuiFlexGroup direction="column">
<EuiFlexItem>
<EuiInMemoryTable<WorkflowResource>
items={allResources}
rowHeader="id"
columns={[
...columns,
{
name: 'Actions',
width: '20%',

render: (row: WorkflowResource) => (
<EuiIcon
onClick={() => openFlyout(row)}
type="inspect"
size="m"
style={{ cursor: 'pointer' }}
/>
),
},
]}
sorting={sorting}
pagination={true}
message={'No existing resources found'}
/>
</EuiFlexItem>
</EuiFlexGroup>
{isFlyoutVisible && (
<EuiFlyout onClose={closeFlyout}>
<EuiFlyoutHeader>
<EuiTitle>
<h2>{selectedRowData?.id}</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={true}>
<EuiText size="m">
<h4>Resource details</h4>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{!rowErrorMessage && !loading ? (
<EuiCodeBlock
language="json"
fontSize="m"
isCopyable={true}
overflowHeight={600}
>
{resourceDetails}
</EuiCodeBlock>
) : loading ? (
<EuiEmptyPrompt
icon={<EuiLoadingSpinner size="xl" />}
title={<h2>Loading</h2>}
/>
) : (
<EuiEmptyPrompt
iconType="alert"
iconColor="danger"
title={<h2>Error loading resource details</h2>}
body={<p>{rowErrorMessage}</p>}
/>
)}
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutBody>
</EuiFlyout>
)}
</>
);
}
4 changes: 2 additions & 2 deletions public/pages/workflow_detail/tools/resources/resources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
EuiText,
} from '@elastic/eui';
import { Workflow } from '../../../../../common';
import { ResourceList } from '../../../../general_components';
import { ResourceListWithFlyout } from './resource_list_with_flyout';

interface ResourcesProps {
workflow?: Workflow;
Expand All @@ -29,7 +29,7 @@ export function Resources(props: ResourcesProps) {
<>
<EuiFlexGroup direction="row">
<EuiFlexItem>
<ResourceList workflow={props.workflow} />
<ResourceListWithFlyout workflow={props.workflow} />
</EuiFlexItem>
</EuiFlexGroup>
</>
Expand Down
Loading

0 comments on commit 9c5026e

Please sign in to comment.