Skip to content

Commit 34ff91e

Browse files
Add functional buttons in form headers; fix query parse bug (#649) (#658)
* Add test flow button in form headers * Refactor flyout into standalone component * More refactoring and layout updates * Move datasourceversion fetching into custom reusable hook * Remove unnecessary loading state param * More refactoring; set up multi-resource flyout * Get multi resource flyout working and entrypoints in main flow * Fix regex replacement * Add examples for ingest and search * Propagate runtime props to flyout content * Propagate in existing resources tab alsog --------- (cherry picked from commit 4bfd70c) Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com> 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>
1 parent 56e301f commit 34ff91e

10 files changed

+620
-179
lines changed

public/pages/workflow_detail/tools/query/query.tsx

+2-12
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ import {
3636
containsEmptyValues,
3737
containsSameValues,
3838
getDataSourceId,
39-
getEffectiveVersion,
4039
getPlaceholdersFromQuery,
4140
getSearchPipelineErrors,
4241
injectParameters,
42+
useDataSourceVersion,
4343
} from '../../../../utils';
4444
import { QueryParamsList, Results } from '../../../../general_components';
4545

@@ -65,17 +65,7 @@ const SEARCH_OPTIONS = [
6565
export function Query(props: QueryProps) {
6666
const dispatch = useAppDispatch();
6767
const dataSourceId = getDataSourceId();
68-
const [dataSourceVersion, setDataSourceVersion] = useState<
69-
string | undefined
70-
>(undefined);
71-
useEffect(() => {
72-
async function getVersion() {
73-
if (dataSourceId !== undefined) {
74-
setDataSourceVersion(await getEffectiveVersion(dataSourceId));
75-
}
76-
}
77-
getVersion();
78-
}, [dataSourceId]);
68+
const dataSourceVersion = useDataSourceVersion(dataSourceId);
7969

8070
const { loading } = useSelector((state: AppState) => state.opensearch);
8171

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiFlyout,
9+
EuiFlyoutBody,
10+
EuiFlyoutHeader,
11+
EuiTitle,
12+
} from '@elastic/eui';
13+
import { WorkflowResource } from '../../../../../common';
14+
import { ResourceFlyoutContent } from './resource_flyout_content';
15+
16+
interface ResourceFlyoutProps {
17+
resource: WorkflowResource;
18+
resourceDetails: string;
19+
onClose: () => void;
20+
errorMessage?: string;
21+
indexName?: string;
22+
searchPipelineName?: string;
23+
ingestPipelineName?: string;
24+
searchQuery?: string;
25+
}
26+
27+
/**
28+
* A simple flyout to display details for a particular workflow resource.
29+
*/
30+
export function ResourceFlyout(props: ResourceFlyoutProps) {
31+
return (
32+
<EuiFlyout onClose={props.onClose}>
33+
<EuiFlyoutHeader>
34+
<EuiTitle>
35+
<h2>Resource details</h2>
36+
</EuiTitle>
37+
</EuiFlyoutHeader>
38+
<EuiFlyoutBody>
39+
<ResourceFlyoutContent
40+
resource={props.resource}
41+
resourceDetails={props.resourceDetails}
42+
errorMessage={props.errorMessage}
43+
indexName={props.indexName}
44+
ingestPipelineName={props.ingestPipelineName}
45+
searchPipelineName={props.searchPipelineName}
46+
searchQuery={props.searchQuery}
47+
/>
48+
</EuiFlyoutBody>
49+
</EuiFlyout>
50+
);
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import React from 'react';
7+
import {
8+
EuiCodeBlock,
9+
EuiFlexGroup,
10+
EuiFlexItem,
11+
EuiTitle,
12+
EuiText,
13+
EuiEmptyPrompt,
14+
EuiHealth,
15+
EuiSpacer,
16+
EuiLink,
17+
} from '@elastic/eui';
18+
import {
19+
BULK_API_DOCS_LINK,
20+
SEARCH_PIPELINE_DOCS_LINK,
21+
WORKFLOW_STEP_TYPE,
22+
WorkflowResource,
23+
} from '../../../../../common';
24+
25+
interface ResourceFlyoutContentProps {
26+
resource: WorkflowResource;
27+
resourceDetails: string;
28+
errorMessage?: string;
29+
indexName?: string;
30+
searchPipelineName?: string;
31+
ingestPipelineName?: string;
32+
searchQuery?: string;
33+
}
34+
35+
/**
36+
* The static flyout content for a particular workflow resource.
37+
*/
38+
export function ResourceFlyoutContent(props: ResourceFlyoutContentProps) {
39+
return (
40+
<EuiFlexGroup direction="column" gutterSize="xs">
41+
<EuiFlexItem grow={false}>
42+
<EuiTitle size="s">
43+
<h4>Name</h4>
44+
</EuiTitle>
45+
</EuiFlexItem>
46+
<EuiFlexItem grow={false}>
47+
<EuiText>{props.resource?.id || ''}</EuiText>
48+
</EuiFlexItem>
49+
<EuiSpacer size="s" />
50+
<EuiFlexItem grow={false}>
51+
<EuiTitle size="s">
52+
<h4>Status</h4>
53+
</EuiTitle>
54+
</EuiFlexItem>
55+
<EuiFlexItem grow={false}>
56+
<EuiHealth color="success">Active</EuiHealth>
57+
</EuiFlexItem>
58+
<EuiSpacer size="s" />
59+
<EuiFlexItem grow={false}>
60+
<EuiTitle size="s">
61+
<h4>
62+
{props.resource?.stepType ===
63+
WORKFLOW_STEP_TYPE.CREATE_INDEX_STEP_TYPE
64+
? 'Configuration'
65+
: 'Pipeline configuration'}
66+
</h4>
67+
</EuiTitle>
68+
</EuiFlexItem>
69+
<EuiFlexItem grow={true}>
70+
{!props.errorMessage ? (
71+
<EuiCodeBlock
72+
language="json"
73+
fontSize="m"
74+
isCopyable={true}
75+
overflowHeight={600}
76+
>
77+
{props.resourceDetails}
78+
</EuiCodeBlock>
79+
) : (
80+
<EuiEmptyPrompt
81+
iconType="alert"
82+
iconColor="danger"
83+
title={<h2>Error loading resource details</h2>}
84+
body={<p>{props.errorMessage}</p>}
85+
/>
86+
)}
87+
</EuiFlexItem>
88+
<EuiSpacer size="s" />
89+
<EuiFlexItem grow={false}>
90+
<EuiTitle size="s">
91+
<h4>
92+
{props.resource?.stepType ===
93+
WORKFLOW_STEP_TYPE.CREATE_INDEX_STEP_TYPE
94+
? 'Ingest additional data using the bulk API'
95+
: props.resource?.stepType ===
96+
WORKFLOW_STEP_TYPE.CREATE_INGEST_PIPELINE_STEP_TYPE
97+
? 'Ingest additional data using the bulk API'
98+
: 'Apply a search pipeline to your applications'}
99+
</h4>
100+
</EuiTitle>
101+
</EuiFlexItem>
102+
{props.resource?.stepType ===
103+
WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE ? (
104+
<EuiFlexItem grow={false}>
105+
<EuiText size="s">
106+
<p>
107+
You can invoke the search pipeline API in your applications.{' '}
108+
<EuiLink href={SEARCH_PIPELINE_DOCS_LINK} target="_blank">
109+
Learn more
110+
</EuiLink>
111+
</p>
112+
</EuiText>
113+
</EuiFlexItem>
114+
) : (
115+
<EuiFlexItem grow={false}>
116+
<EuiText size="s">
117+
<p>
118+
You can ingest a larger amount of data using the Bulk API.{' '}
119+
<EuiLink href={BULK_API_DOCS_LINK} target="_blank">
120+
Learn more
121+
</EuiLink>
122+
</p>
123+
</EuiText>
124+
</EuiFlexItem>
125+
)}
126+
{props.resource?.stepType ===
127+
WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE ? (
128+
<EuiFlexItem grow={false}>
129+
<EuiCodeBlock fontSize="m" isCopyable={true}>
130+
{`GET /${props.indexName || 'my_index'}/_search?search_pipeline=${
131+
props.searchPipelineName || 'my_pipeline'
132+
}` +
133+
`${
134+
props.searchQuery
135+
? `\n${props.searchQuery}`
136+
: `
137+
{
138+
"query": {
139+
"term": {
140+
"item_text": {
141+
"value": "{{query_text}}"
142+
}
143+
}
144+
}
145+
}`
146+
}`}
147+
</EuiCodeBlock>
148+
</EuiFlexItem>
149+
) : (
150+
<EuiFlexItem grow={false}>
151+
<EuiCodeBlock fontSize="m" isCopyable={true}>
152+
{`POST _bulk
153+
{ "index": { "_index": "${props.indexName || 'my_index'}", "_id": "abc123" } }
154+
{ "my_field_1": "my_field_value_1", "my_field_2": "my_field_value_2" }`}
155+
</EuiCodeBlock>
156+
</EuiFlexItem>
157+
)}
158+
</EuiFlexGroup>
159+
);
160+
}

public/pages/workflow_detail/tools/resources/resource_list_with_flyout.tsx

+29-57
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,17 @@
55

66
import React, { useState, useEffect } from 'react';
77
import { useSelector } from 'react-redux';
8+
import { getIn, useFormikContext } from 'formik';
89
import {
910
Direction,
10-
EuiCodeBlock,
1111
EuiFlexGroup,
1212
EuiFlexItem,
13-
EuiFlyout,
14-
EuiFlyoutBody,
15-
EuiFlyoutHeader,
1613
EuiInMemoryTable,
17-
EuiTitle,
1814
EuiIcon,
19-
EuiText,
20-
EuiEmptyPrompt,
21-
EuiLoadingSpinner,
2215
} from '@elastic/eui';
2316
import {
2417
Workflow,
18+
WorkflowFormValues,
2519
WorkflowResource,
2620
customStringify,
2721
} from '../../../../../common';
@@ -38,22 +32,28 @@ import {
3832
getErrorMessageForStepType,
3933
} from '../../../../utils';
4034
import { columns } from './columns';
35+
import { ResourceFlyout } from './resource_flyout';
4136

4237
interface ResourceListFlyoutProps {
4338
workflow?: Workflow;
4439
}
4540

4641
/**
47-
* The searchable list of resources for a particular workflow.
42+
* The searchable list of resources for a particular workflow. Each resource has an "inspect"
43+
* action to view more details within a flyout.
4844
*/
4945
export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
50-
const [allResources, setAllResources] = useState<WorkflowResource[]>([]);
5146
const dispatch = useAppDispatch();
47+
const { values } = useFormikContext<WorkflowFormValues>();
48+
const [allResources, setAllResources] = useState<WorkflowResource[]>([]);
5249
const dataSourceId = getDataSourceId();
53-
const [resourceDetails, setResourceDetails] = useState<string | null>(null);
54-
const [rowErrorMessage, setRowErrorMessage] = useState<string | null>(null);
50+
const [resourceDetails, setResourceDetails] = useState<string | undefined>(
51+
undefined
52+
);
53+
const [rowErrorMessage, setRowErrorMessage] = useState<string | undefined>(
54+
undefined
55+
);
5556
const {
56-
loading,
5757
getIndexErrorMessage,
5858
getIngestPipelineErrorMessage,
5959
getSearchPipelineErrorMessage,
@@ -112,7 +112,7 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
112112
},
113113
};
114114

115-
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
115+
const [isFlyoutVisible, setIsFlyoutVisible] = useState<boolean>(false);
116116
const [selectedRowData, setSelectedRowData] = useState<
117117
WorkflowResource | undefined
118118
>(undefined);
@@ -140,7 +140,7 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
140140
const closeFlyout = () => {
141141
setIsFlyoutVisible(false);
142142
setSelectedRowData(undefined);
143-
setResourceDetails(null);
143+
setResourceDetails(undefined);
144144
};
145145

146146
return (
@@ -172,48 +172,20 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
172172
/>
173173
</EuiFlexItem>
174174
</EuiFlexGroup>
175-
{isFlyoutVisible && (
176-
<EuiFlyout onClose={closeFlyout}>
177-
<EuiFlyoutHeader>
178-
<EuiTitle>
179-
<h2>{selectedRowData?.id}</h2>
180-
</EuiTitle>
181-
</EuiFlyoutHeader>
182-
<EuiFlyoutBody>
183-
<EuiFlexGroup direction="column" gutterSize="xs">
184-
<EuiFlexItem grow={true}>
185-
<EuiText size="m">
186-
<h4>Resource details</h4>
187-
</EuiText>
188-
</EuiFlexItem>
189-
<EuiFlexItem grow={true}>
190-
{!rowErrorMessage && !loading ? (
191-
<EuiCodeBlock
192-
language="json"
193-
fontSize="m"
194-
isCopyable={true}
195-
overflowHeight={600}
196-
>
197-
{resourceDetails}
198-
</EuiCodeBlock>
199-
) : loading ? (
200-
<EuiEmptyPrompt
201-
icon={<EuiLoadingSpinner size="xl" />}
202-
title={<h2>Loading</h2>}
203-
/>
204-
) : (
205-
<EuiEmptyPrompt
206-
iconType="alert"
207-
iconColor="danger"
208-
title={<h2>Error loading resource details</h2>}
209-
body={<p>{rowErrorMessage}</p>}
210-
/>
211-
)}
212-
</EuiFlexItem>
213-
</EuiFlexGroup>
214-
</EuiFlyoutBody>
215-
</EuiFlyout>
216-
)}
175+
{isFlyoutVisible &&
176+
selectedRowData !== undefined &&
177+
resourceDetails !== undefined && (
178+
<ResourceFlyout
179+
resource={selectedRowData}
180+
resourceDetails={resourceDetails}
181+
onClose={closeFlyout}
182+
errorMessage={rowErrorMessage}
183+
indexName={getIn(values, 'ingest.index.name')}
184+
ingestPipelineName={getIn(values, 'ingest.pipelineName')}
185+
searchPipelineName={getIn(values, 'search.pipelineName')}
186+
searchQuery={getIn(values, 'search.request')}
187+
/>
188+
)}
217189
</>
218190
);
219191
}

0 commit comments

Comments
 (0)