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

[Backport main] Add functional buttons in form headers; fix query parse bug #658

Merged
merged 1 commit into from
Feb 28, 2025
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
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
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
Loading