diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts index 1342c53de528..849cc470ccf5 100644 --- a/web-console/src/components/index.ts +++ b/web-console/src/components/index.ts @@ -51,6 +51,7 @@ export * from './refresh-button/refresh-button'; export * from './rule-editor/rule-editor'; export * from './segment-timeline/segment-timeline'; export * from './show-json/show-json'; +export * from './show-json-or-stages/show-json-or-stages'; export * from './show-log/show-log'; export * from './show-value/show-value'; export * from './splitter-layout/splitter-layout'; diff --git a/web-console/src/components/show-json-or-stages/__snapshots__/show-json-or-stages.spec.tsx.snap b/web-console/src/components/show-json-or-stages/__snapshots__/show-json-or-stages.spec.tsx.snap new file mode 100644 index 000000000000..052bb7e9d6c9 --- /dev/null +++ b/web-console/src/components/show-json-or-stages/__snapshots__/show-json-or-stages.spec.tsx.snap @@ -0,0 +1,89 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ShowJsonOrStages matches snapshot 1`] = ` +
+
+
+ + + +
+
+
+
+ +
+
+
+`; diff --git a/web-console/src/components/show-json-or-stages/show-json-or-stages.scss b/web-console/src/components/show-json-or-stages/show-json-or-stages.scss new file mode 100644 index 000000000000..f4c7c60e34ce --- /dev/null +++ b/web-console/src/components/show-json-or-stages/show-json-or-stages.scss @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.show-json-or-stages { + position: relative; + height: 100%; + + .top-actions { + text-align: right; + padding-bottom: 10px; + + & > * { + display: inline-block; + } + } + + .main-area { + position: absolute; + width: 100%; + top: 40px; + bottom: 0; + + textarea { + height: 100%; + width: 100%; + resize: none; + } + + .loader { + position: relative; + } + + .execution-stages-pane { + height: 100%; + } + } +} diff --git a/web-console/src/components/show-json-or-stages/show-json-or-stages.spec.tsx b/web-console/src/components/show-json-or-stages/show-json-or-stages.spec.tsx new file mode 100644 index 000000000000..32660e6b3df8 --- /dev/null +++ b/web-console/src/components/show-json-or-stages/show-json-or-stages.spec.tsx @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render } from '@testing-library/react'; + +import { ShowJsonOrStages } from './show-json-or-stages'; + +describe('ShowJsonOrStages', () => { + it('matches snapshot', () => { + const showJsonOrStages = ; + const { container } = render(showJsonOrStages); + expect(container.firstChild).toMatchSnapshot(); + }); +}); diff --git a/web-console/src/components/show-json-or-stages/show-json-or-stages.tsx b/web-console/src/components/show-json-or-stages/show-json-or-stages.tsx new file mode 100644 index 000000000000..8010104bceea --- /dev/null +++ b/web-console/src/components/show-json-or-stages/show-json-or-stages.tsx @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Button, ButtonGroup, Intent } from '@blueprintjs/core'; +import copy from 'copy-to-clipboard'; +import * as JSONBig from 'json-bigint-native'; +import React from 'react'; +import AceEditor from 'react-ace'; + +import { Execution } from '../../druid-models'; +import { useQueryManager } from '../../hooks'; +import { Api, AppToaster, UrlBaser } from '../../singletons'; +import { downloadFile } from '../../utils'; +import { ExecutionStagesPane } from '../../views/workbench-view/execution-stages-pane/execution-stages-pane'; +import { Loader } from '../loader/loader'; + +import './show-json-or-stages.scss'; + +export interface ShowJsonOrStagesProps { + endpoint: string; + transform?: (x: any) => any; + downloadFilename?: string; +} + +export const ShowJsonOrStages = React.memo(function ShowJsonOrStages(props: ShowJsonOrStagesProps) { + const { endpoint, transform, downloadFilename } = props; + + const [jsonState] = useQueryManager({ + processQuery: async (_, signal) => { + const resp = await Api.instance.get(endpoint, { signal }); + let data = resp.data; + if (transform) data = transform(data); + + let execution: Execution | undefined; + if (data.multiStageQuery) { + try { + execution = Execution.fromTaskReport(data); + } catch (e) { + console.error(`Could not parse task report as MSQ execution: ${e.message}`); + } + } + + return [ + typeof data === 'string' ? data : JSONBig.stringify(data, undefined, 2), + execution, + ] as [string, Execution | undefined]; + }, + initQuery: null, + }); + + const [jsonValue, execution] = jsonState.data || ['']; + return ( +
+
+ + {downloadFilename && ( +
+ +
+ {jsonState.loading ? ( + + ) : execution ? ( + + ) : ( + + )} +
+
+ ); +}); diff --git a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx index 9edc5d996f47..56389714ae4e 100644 --- a/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx +++ b/web-console/src/dialogs/task-table-action-dialog/task-table-action-dialog.tsx @@ -18,7 +18,7 @@ import React, { useState } from 'react'; -import { ShowJson, ShowLog } from '../../components'; +import { ShowJson, ShowJsonOrStages, ShowLog } from '../../components'; import { Api } from '../../singletons'; import { deepGet } from '../../utils'; import type { BasicAction } from '../../utils/basic-action'; @@ -83,7 +83,7 @@ export const TaskTableActionDialog = React.memo(function TaskTableActionDialog( /> )} {activeTab === 'report' && ( - deepGet(x, 'ingestionStatsAndErrors.payload') || x} downloadFilename={`task-reports-${taskId}.json`} diff --git a/web-console/src/views/workbench-view/execution-stages-pane/execution-stages-pane.tsx b/web-console/src/views/workbench-view/execution-stages-pane/execution-stages-pane.tsx index d7c447d090cf..e4717c6d6918 100644 --- a/web-console/src/views/workbench-view/execution-stages-pane/execution-stages-pane.tsx +++ b/web-console/src/views/workbench-view/execution-stages-pane/execution-stages-pane.tsx @@ -157,7 +157,7 @@ export interface ExecutionStagesPaneProps { execution: Execution; onErrorClick?(): void; onWarningClick?(): void; - goToTask(taskId: string): void; + goToTask?(taskId: string): void; } export const ExecutionStagesPane = React.memo(function ExecutionStagesPane( @@ -245,8 +245,10 @@ export const ExecutionStagesPane = React.memo(function ExecutionStagesPane( Header: 'Worker', id: 'worker', accessor: d => d.index, + className: goToTask ? undefined : 'padded', width: 95, Cell({ value }) { + if (!goToTask) return `Worker${value}`; const taskId = `${execution.id}-worker${value}_0`; return (