Skip to content

Commit

Permalink
Add functional buttons in form headers; fix query parse bug (#649)
Browse files Browse the repository at this point in the history
* Add test flow button in form headers

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Refactor flyout into standalone component

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* More refactoring and layout updates

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Move datasourceversion fetching into custom reusable hook

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Remove unnecessary loading state param

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* More refactoring; set up multi-resource flyout

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Get multi resource flyout working and entrypoints in main flow

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Fix regex replacement

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Add examples for ingest and search

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Propagate runtime props to flyout content

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

* Propagate in existing resources tab alsog

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>

---------

Signed-off-by: Tyler Ohlsen <ohltyler@amazon.com>
  • Loading branch information
ohltyler authored Feb 28, 2025
1 parent 372ea42 commit 4bfd70c
Show file tree
Hide file tree
Showing 10 changed files with 620 additions and 179 deletions.
14 changes: 2 additions & 12 deletions public/pages/workflow_detail/tools/query/query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ import {
containsEmptyValues,
containsSameValues,
getDataSourceId,
getEffectiveVersion,
getPlaceholdersFromQuery,
getSearchPipelineErrors,
injectParameters,
useDataSourceVersion,
} from '../../../../utils';
import { QueryParamsList, Results } from '../../../../general_components';

Expand All @@ -65,17 +65,7 @@ const SEARCH_OPTIONS = [
export function Query(props: QueryProps) {
const dispatch = useAppDispatch();
const dataSourceId = getDataSourceId();
const [dataSourceVersion, setDataSourceVersion] = useState<
string | undefined
>(undefined);
useEffect(() => {
async function getVersion() {
if (dataSourceId !== undefined) {
setDataSourceVersion(await getEffectiveVersion(dataSourceId));
}
}
getVersion();
}, [dataSourceId]);
const dataSourceVersion = useDataSourceVersion(dataSourceId);

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

Expand Down
51 changes: 51 additions & 0 deletions public/pages/workflow_detail/tools/resources/resource_flyout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiTitle,
} from '@elastic/eui';
import { WorkflowResource } from '../../../../../common';
import { ResourceFlyoutContent } from './resource_flyout_content';

interface ResourceFlyoutProps {
resource: WorkflowResource;
resourceDetails: string;
onClose: () => void;
errorMessage?: string;
indexName?: string;
searchPipelineName?: string;
ingestPipelineName?: string;
searchQuery?: string;
}

/**
* A simple flyout to display details for a particular workflow resource.
*/
export function ResourceFlyout(props: ResourceFlyoutProps) {
return (
<EuiFlyout onClose={props.onClose}>
<EuiFlyoutHeader>
<EuiTitle>
<h2>Resource details</h2>
</EuiTitle>
</EuiFlyoutHeader>
<EuiFlyoutBody>
<ResourceFlyoutContent
resource={props.resource}
resourceDetails={props.resourceDetails}
errorMessage={props.errorMessage}
indexName={props.indexName}
ingestPipelineName={props.ingestPipelineName}
searchPipelineName={props.searchPipelineName}
searchQuery={props.searchQuery}
/>
</EuiFlyoutBody>
</EuiFlyout>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import {
EuiCodeBlock,
EuiFlexGroup,
EuiFlexItem,
EuiTitle,
EuiText,
EuiEmptyPrompt,
EuiHealth,
EuiSpacer,
EuiLink,
} from '@elastic/eui';
import {
BULK_API_DOCS_LINK,
SEARCH_PIPELINE_DOCS_LINK,
WORKFLOW_STEP_TYPE,
WorkflowResource,
} from '../../../../../common';

interface ResourceFlyoutContentProps {
resource: WorkflowResource;
resourceDetails: string;
errorMessage?: string;
indexName?: string;
searchPipelineName?: string;
ingestPipelineName?: string;
searchQuery?: string;
}

/**
* The static flyout content for a particular workflow resource.
*/
export function ResourceFlyoutContent(props: ResourceFlyoutContentProps) {
return (
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h4>Name</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText>{props.resource?.id || ''}</EuiText>
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h4>Status</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiHealth color="success">Active</EuiHealth>
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h4>
{props.resource?.stepType ===
WORKFLOW_STEP_TYPE.CREATE_INDEX_STEP_TYPE
? 'Configuration'
: 'Pipeline configuration'}
</h4>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={true}>
{!props.errorMessage ? (
<EuiCodeBlock
language="json"
fontSize="m"
isCopyable={true}
overflowHeight={600}
>
{props.resourceDetails}
</EuiCodeBlock>
) : (
<EuiEmptyPrompt
iconType="alert"
iconColor="danger"
title={<h2>Error loading resource details</h2>}
body={<p>{props.errorMessage}</p>}
/>
)}
</EuiFlexItem>
<EuiSpacer size="s" />
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h4>
{props.resource?.stepType ===
WORKFLOW_STEP_TYPE.CREATE_INDEX_STEP_TYPE
? 'Ingest additional data using the bulk API'
: props.resource?.stepType ===
WORKFLOW_STEP_TYPE.CREATE_INGEST_PIPELINE_STEP_TYPE
? 'Ingest additional data using the bulk API'
: 'Apply a search pipeline to your applications'}
</h4>
</EuiTitle>
</EuiFlexItem>
{props.resource?.stepType ===
WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE ? (
<EuiFlexItem grow={false}>
<EuiText size="s">
<p>
You can invoke the search pipeline API in your applications.{' '}
<EuiLink href={SEARCH_PIPELINE_DOCS_LINK} target="_blank">
Learn more
</EuiLink>
</p>
</EuiText>
</EuiFlexItem>
) : (
<EuiFlexItem grow={false}>
<EuiText size="s">
<p>
You can ingest a larger amount of data using the Bulk API.{' '}
<EuiLink href={BULK_API_DOCS_LINK} target="_blank">
Learn more
</EuiLink>
</p>
</EuiText>
</EuiFlexItem>
)}
{props.resource?.stepType ===
WORKFLOW_STEP_TYPE.CREATE_SEARCH_PIPELINE_STEP_TYPE ? (
<EuiFlexItem grow={false}>
<EuiCodeBlock fontSize="m" isCopyable={true}>
{`GET /${props.indexName || 'my_index'}/_search?search_pipeline=${
props.searchPipelineName || 'my_pipeline'
}` +
`${
props.searchQuery
? `\n${props.searchQuery}`
: `
{
"query": {
"term": {
"item_text": {
"value": "{{query_text}}"
}
}
}
}`
}`}
</EuiCodeBlock>
</EuiFlexItem>
) : (
<EuiFlexItem grow={false}>
<EuiCodeBlock fontSize="m" isCopyable={true}>
{`POST _bulk
{ "index": { "_index": "${props.indexName || 'my_index'}", "_id": "abc123" } }
{ "my_field_1": "my_field_value_1", "my_field_2": "my_field_value_2" }`}
</EuiCodeBlock>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,17 @@

import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { getIn, useFormikContext } from 'formik';
import {
Direction,
EuiCodeBlock,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutHeader,
EuiInMemoryTable,
EuiTitle,
EuiIcon,
EuiText,
EuiEmptyPrompt,
EuiLoadingSpinner,
} from '@elastic/eui';
import {
Workflow,
WorkflowFormValues,
WorkflowResource,
customStringify,
} from '../../../../../common';
Expand All @@ -38,22 +32,28 @@ import {
getErrorMessageForStepType,
} from '../../../../utils';
import { columns } from './columns';
import { ResourceFlyout } from './resource_flyout';

interface ResourceListFlyoutProps {
workflow?: Workflow;
}

/**
* The searchable list of resources for a particular workflow.
* The searchable list of resources for a particular workflow. Each resource has an "inspect"
* action to view more details within a flyout.
*/
export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
const [allResources, setAllResources] = useState<WorkflowResource[]>([]);
const dispatch = useAppDispatch();
const { values } = useFormikContext<WorkflowFormValues>();
const [allResources, setAllResources] = useState<WorkflowResource[]>([]);
const dataSourceId = getDataSourceId();
const [resourceDetails, setResourceDetails] = useState<string | null>(null);
const [rowErrorMessage, setRowErrorMessage] = useState<string | null>(null);
const [resourceDetails, setResourceDetails] = useState<string | undefined>(
undefined
);
const [rowErrorMessage, setRowErrorMessage] = useState<string | undefined>(
undefined
);
const {
loading,
getIndexErrorMessage,
getIngestPipelineErrorMessage,
getSearchPipelineErrorMessage,
Expand Down Expand Up @@ -112,7 +112,7 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
},
};

const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
const [isFlyoutVisible, setIsFlyoutVisible] = useState<boolean>(false);
const [selectedRowData, setSelectedRowData] = useState<
WorkflowResource | undefined
>(undefined);
Expand Down Expand Up @@ -140,7 +140,7 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
const closeFlyout = () => {
setIsFlyoutVisible(false);
setSelectedRowData(undefined);
setResourceDetails(null);
setResourceDetails(undefined);
};

return (
Expand Down Expand Up @@ -172,48 +172,20 @@ export function ResourceListWithFlyout(props: ResourceListFlyoutProps) {
/>
</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>
)}
{isFlyoutVisible &&
selectedRowData !== undefined &&
resourceDetails !== undefined && (
<ResourceFlyout
resource={selectedRowData}
resourceDetails={resourceDetails}
onClose={closeFlyout}
errorMessage={rowErrorMessage}
indexName={getIn(values, 'ingest.index.name')}
ingestPipelineName={getIn(values, 'ingest.pipelineName')}
searchPipelineName={getIn(values, 'search.pipelineName')}
searchQuery={getIn(values, 'search.request')}
/>
)}
</>
);
}
Loading

0 comments on commit 4bfd70c

Please sign in to comment.