Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added drill-down details for workflow-created resources #11

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ import {
WorkflowResource,
customStringify,
} from '../../../../../common';
import { fetchResourceData } from '../../../../utils';
import { AppState, useAppDispatch } from '../../../../store';
import { getDataSourceId } from '../../../../utils';
import {
AppState,
useAppDispatch,
getIndex,
getIngestPipeline,
getSearchPipeline,
} from '../../../../store';
import {
extractIdsByStepType,
getDataSourceId,
getErrorMessageForStepType,
} from '../../../../utils';
import { columns } from './columns';
import { useSelector } from 'react-redux';

Expand All @@ -42,7 +51,16 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();
const [resourceDetails, setResourceDetails] = useState<string | null>(null);
const loading = useSelector((state: AppState) => state.opensearch.loading);
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
Expand All @@ -57,6 +75,36 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
}
}, [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',
Expand All @@ -73,10 +121,19 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
const openFlyout = async (row: WorkflowResource) => {
setSelectedRowData(row);
setIsFlyoutVisible(true);
try {
const result = await fetchResourceData(row, dataSourceId!, dispatch);
setResourceDetails(customStringify(result));
} catch (error) {}
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.
Expand Down Expand Up @@ -130,7 +187,7 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{resourceDetails && !loading ? (
{!rowErrorMessage && !loading ? (
<EuiCodeBlock
language="json"
fontSize="m"
Expand All @@ -139,11 +196,18 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
>
{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>
Expand Down
86 changes: 69 additions & 17 deletions public/pages/workflows/workflow_list/resource_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@ import {
WorkflowResource,
customStringify,
} from '../../../../common';
import { AppState, useAppDispatch } from '../../../store';
import { fetchResourceData, getDataSourceId } from '../../../utils';
import {
AppState,
getIndex,
getIngestPipeline,
getSearchPipeline,
useAppDispatch,
} from '../../../store';
import {
extractIdsByStepType,
getDataSourceId,
getErrorMessageForStepType,
} from '../../../utils';
import { useSelector } from 'react-redux';
import { isEmpty } from 'lodash';

interface ResourceListProps {
workflow?: Workflow;
Expand All @@ -41,7 +50,15 @@ export function ResourceList(props: ResourceListProps) {
const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{
[key: string]: any;
}>({});
const loading = useSelector((state: AppState) => state.opensearch.loading);
const {
loading,
getIndexErrorMessage,
getIngestPipelineErrorMessage,
getSearchPipelineErrorMessage,
indexDetails,
ingestPipelineDetails,
searchPipelineDetails,
} = useSelector((state: AppState) => state.opensearch);
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<keyof WorkflowResource>('id');
Expand All @@ -60,17 +77,47 @@ export function ResourceList(props: ResourceListProps) {
}
}, [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]);

// Renders the expanded row to show resource details in a code block.
const renderExpandedRow = useCallback(
(data: any) => (
(data: any, resourceDetailsErrorMessage?: string) => (
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={true} style={{ paddingLeft: '20px' }}>
<EuiText size="m">
<h4>Resource details</h4>
</EuiText>
</EuiFlexItem>
<EuiFlexItem grow={true} style={{ paddingLeft: '20px' }}>
{!data.startsWith('error:') && !loading ? (
{!resourceDetailsErrorMessage && !loading ? (
<EuiCodeBlock
language="json"
fontSize="m"
Expand All @@ -89,7 +136,7 @@ export function ResourceList(props: ResourceListProps) {
iconType="alert"
iconColor="danger"
title={<h2>Error loading resource details</h2>}
body={<p>{data.replace(/^error:\s*/, '')}</p>}
body={<p>{resourceDetailsErrorMessage}</p>}
/>
)}
</EuiFlexItem>
Expand All @@ -106,18 +153,23 @@ export function ResourceList(props: ResourceListProps) {
delete updatedItemIdToExpandedRowMap[item.id];
setItemIdToExpandedRowMap(updatedItemIdToExpandedRowMap);
} else {
try {
const result = await fetchResourceData(item, dataSourceId!, dispatch);
if (result) {
setItemIdToExpandedRowMap((prevMap) => ({
...prevMap,
[item.id]: renderExpandedRow(result),
}));
}
} catch (error) {
const value =
indexDetails[item.id] ??
ingestPipelineDetails[item.id] ??
searchPipelineDetails[item.id] ??
'';
const resourceDetailsErrorMessage = getErrorMessageForStepType(
item.stepType,
getIndexErrorMessage,
getIngestPipelineErrorMessage,
getSearchPipelineErrorMessage
);

const result = { [item.id]: value };
if (result) {
setItemIdToExpandedRowMap((prevMap) => ({
...prevMap,
[item.id]: renderExpandedRow('error:' + error),
[item.id]: renderExpandedRow(result, resourceDetailsErrorMessage),
}));
}
}
Expand Down
54 changes: 45 additions & 9 deletions public/store/reducers/opensearch_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { getRouteService } from '../../services';
import {
Index,
IndexConfiguration,
IndexResponse,
IngestPipelineConfig,
IngestPipelineResponse,
SearchPipelineConfig,
SearchPipelineResponse,
OMIT_SYSTEM_INDEX_PATTERN,
SimulateIngestPipelineDoc,
} from '../../../common';
Expand All @@ -16,7 +21,13 @@ import { HttpFetchError } from '../../../../../src/core/public';
export const INITIAL_OPENSEARCH_STATE = {
loading: false,
errorMessage: '',
getIndexErrorMessage: '',
getSearchPipelineErrorMessage: '',
getIngestPipelineErrorMessage: '',
indices: {} as { [key: string]: Index },
indexDetails: {} as { [key: string]: IndexConfiguration },
ingestPipelineDetails: {} as { [key: string]: IngestPipelineConfig },
searchPipelineDetails: {} as { [key: string]: SearchPipelineConfig },
};

const OPENSEARCH_PREFIX = 'opensearch';
Expand Down Expand Up @@ -276,15 +287,15 @@ const opensearchSlice = createSlice({
})
.addCase(getIndex.pending, (state, action) => {
state.loading = true;
state.errorMessage = '';
state.getIndexErrorMessage = '';
})
.addCase(getIngestPipeline.pending, (state, action) => {
state.loading = true;
state.errorMessage = '';
state.getIngestPipelineErrorMessage = '';
})
.addCase(getSearchPipeline.pending, (state, action) => {
state.loading = true;
state.errorMessage = '';
state.getSearchPipelineErrorMessage = '';
})
.addCase(searchIndex.pending, (state, action) => {
state.loading = true;
Expand All @@ -308,16 +319,41 @@ const opensearchSlice = createSlice({
state.errorMessage = '';
})
.addCase(getIndex.fulfilled, (state, action) => {
const resourceDetailsMap = new Map<string, IndexConfiguration>();
action.payload.forEach((index: IndexResponse) => {
resourceDetailsMap.set(index.indexName, index.indexDetails);
});
state.indexDetails = Object.fromEntries(resourceDetailsMap.entries());
state.loading = false;
state.errorMessage = '';
state.getIndexErrorMessage = '';
})
.addCase(getSearchPipeline.fulfilled, (state, action) => {
const resourceDetailsMap = new Map<string, SearchPipelineConfig>();
action.payload.forEach((pipeline: SearchPipelineResponse) => {
resourceDetailsMap.set(
pipeline.pipelineId,
pipeline.searchPipelineDetails
);
});
state.searchPipelineDetails = Object.fromEntries(
resourceDetailsMap.entries()
);
state.loading = false;
state.errorMessage = '';
state.getSearchPipelineErrorMessage = '';
})
.addCase(getIngestPipeline.fulfilled, (state, action) => {
const resourceDetailsMap = new Map<string, IngestPipelineConfig>();
action.payload.forEach((pipeline: IngestPipelineResponse) => {
resourceDetailsMap.set(
pipeline.pipelineId,
pipeline.ingestPipelineDetails
);
});
state.ingestPipelineDetails = Object.fromEntries(
resourceDetailsMap.entries()
);
state.loading = false;
state.errorMessage = '';
state.getIngestPipelineErrorMessage = '';
})
.addCase(searchIndex.fulfilled, (state, action) => {
state.loading = false;
Expand All @@ -336,15 +372,15 @@ const opensearchSlice = createSlice({
state.loading = false;
})
.addCase(getIndex.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.getIndexErrorMessage = action.payload as string;
state.loading = false;
})
.addCase(getIngestPipeline.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.getIngestPipelineErrorMessage = action.payload as string;
state.loading = false;
})
.addCase(getSearchPipeline.rejected, (state, action) => {
state.errorMessage = action.payload as string;
state.getSearchPipelineErrorMessage = action.payload as string;
state.loading = false;
})
.addCase(searchIndex.rejected, (state, action) => {
Expand Down
Loading
Loading