From 724cf5294f5238b65ad39e8834aeec381a7ab52c Mon Sep 17 00:00:00 2001 From: Andrii Vorobiov Date: Wed, 13 Jan 2021 13:03:37 +0200 Subject: [PATCH] ui: reuse components from admin-ui-components package Currently, many components were exported to `admin-ui-components` package and these components weren't removed from this repo. To avoid duplicates, these components removed and re-imported back. Release note: None --- .../clusterviz/containers/map/breadcrumbs.tsx | 4 +- .../components/downloadFile/downloadFile.tsx | 86 --- pkg/ui/src/components/downloadFile/index.ts | 11 - pkg/ui/src/components/index.ts | 2 - pkg/ui/src/components/table/index.ts | 11 - pkg/ui/src/components/table/table.styl | 97 --- pkg/ui/src/components/table/table.tsx | 63 -- pkg/ui/src/redux/nodes.ts | 5 +- pkg/ui/src/util/intersperse.spec.ts | 30 - pkg/ui/src/util/intersperse.ts | 21 - .../containers/nodesOverview/index.tsx | 15 +- .../nodeHistory/decommissionedNodeHistory.tsx | 3 +- .../statementDiagnosticsHistory/index.tsx | 20 +- pkg/ui/src/views/sessions/sessionDetails.tsx | 2 +- pkg/ui/src/views/sessions/sessionsTable.tsx | 2 +- .../views/statements/barCharts.module.styl | 77 --- .../views/statements/barCharts.stories.tsx | 105 ---- pkg/ui/src/views/statements/barCharts.tsx | 581 ------------------ .../diagnostics/diagnosticsUtils.ts | 79 --- .../diagnostics/diagnosticsView.module.styl | 66 -- .../diagnostics/diagnosticsView.spec.tsx | 130 ---- .../diagnostics/diagnosticsView.tsx | 274 --------- .../src/views/statements/diagnostics/index.ts | 6 +- pkg/ui/src/views/statements/planView/index.ts | 11 - .../statements/planView/planView.fixtures.tsx | 193 ------ .../statements/planView/planView.module.styl | 199 ------ .../statements/planView/planView.spec.tsx | 415 ------------- .../statements/planView/planView.stories.tsx | 18 - .../views/statements/planView/planView.tsx | 373 ----------- .../src/views/statements/statementDetails.tsx | 2 +- .../statements/statementsTable.module.styl | 50 -- .../statements/statementsTable.stories.tsx | 39 -- .../src/views/statements/statementsTable.tsx | 180 ------ .../statements/statementsTableContent.tsx | 291 --------- 34 files changed, 28 insertions(+), 3433 deletions(-) delete mode 100644 pkg/ui/src/components/downloadFile/downloadFile.tsx delete mode 100644 pkg/ui/src/components/downloadFile/index.ts delete mode 100644 pkg/ui/src/components/table/index.ts delete mode 100644 pkg/ui/src/components/table/table.styl delete mode 100644 pkg/ui/src/components/table/table.tsx delete mode 100644 pkg/ui/src/util/intersperse.spec.ts delete mode 100644 pkg/ui/src/util/intersperse.ts delete mode 100644 pkg/ui/src/views/statements/barCharts.module.styl delete mode 100644 pkg/ui/src/views/statements/barCharts.stories.tsx delete mode 100644 pkg/ui/src/views/statements/barCharts.tsx delete mode 100644 pkg/ui/src/views/statements/diagnostics/diagnosticsUtils.ts delete mode 100644 pkg/ui/src/views/statements/diagnostics/diagnosticsView.module.styl delete mode 100644 pkg/ui/src/views/statements/diagnostics/diagnosticsView.spec.tsx delete mode 100644 pkg/ui/src/views/statements/diagnostics/diagnosticsView.tsx delete mode 100644 pkg/ui/src/views/statements/planView/index.ts delete mode 100644 pkg/ui/src/views/statements/planView/planView.fixtures.tsx delete mode 100644 pkg/ui/src/views/statements/planView/planView.module.styl delete mode 100644 pkg/ui/src/views/statements/planView/planView.spec.tsx delete mode 100644 pkg/ui/src/views/statements/planView/planView.stories.tsx delete mode 100644 pkg/ui/src/views/statements/planView/planView.tsx delete mode 100644 pkg/ui/src/views/statements/statementsTable.module.styl delete mode 100644 pkg/ui/src/views/statements/statementsTable.stories.tsx delete mode 100644 pkg/ui/src/views/statements/statementsTable.tsx delete mode 100644 pkg/ui/src/views/statements/statementsTableContent.tsx diff --git a/pkg/ui/ccl/src/views/clusterviz/containers/map/breadcrumbs.tsx b/pkg/ui/ccl/src/views/clusterviz/containers/map/breadcrumbs.tsx index d092a24aea9e..322488863530 100644 --- a/pkg/ui/ccl/src/views/clusterviz/containers/map/breadcrumbs.tsx +++ b/pkg/ui/ccl/src/views/clusterviz/containers/map/breadcrumbs.tsx @@ -12,7 +12,7 @@ import { Link } from "react-router-dom"; import { generateLocalityRoute } from "src/util/localities"; import { LocalityTier } from "src/redux/localities"; -import { intersperse } from "src/util/intersperse"; +import { util } from "@cockroachlabs/admin-ui-components"; import { getLocalityLabel } from "src/util/localities"; import mapPinIcon from "!!raw-loader!assets/mapPin.svg"; import { trustIcon } from "src/util/trust"; @@ -24,6 +24,8 @@ interface BreadcrumbsProps { tiers: LocalityTier[]; } +const { intersperse } = util; + export class Breadcrumbs extends React.Component { render() { const paths = breadcrumbPaths(this.props.tiers); diff --git a/pkg/ui/src/components/downloadFile/downloadFile.tsx b/pkg/ui/src/components/downloadFile/downloadFile.tsx deleted file mode 100644 index 0eedae621d32..000000000000 --- a/pkg/ui/src/components/downloadFile/downloadFile.tsx +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React, { - useRef, - useEffect, - forwardRef, - useImperativeHandle, -} from "react"; - -type FileTypes = "text/plain" | "application/json"; - -export interface DownloadAsFileProps { - fileName?: string; - fileType?: FileTypes; - content?: string; -} - -export interface DownloadFileRef { - download: (name: string, type: FileTypes, body: string) => void; -} - -/* - * DownloadFile can download file in two modes `default` and `imperative`. - * `Default` mode - when DownloadFile wraps component which should trigger - * downloading and can work only if content of file is already available. - * - * For example: - * ``` - * - * - * ``` - * */ -export const DownloadFile = forwardRef( - (props, ref) => { - const { children, fileName, fileType, content } = props; - const anchorRef = useRef(); - - const bootstrapFile = (name: string, type: FileTypes, body: string) => { - const anchorElement = anchorRef.current; - const file = new Blob([body], { type }); - anchorElement.href = URL.createObjectURL(file); - anchorElement.download = name; - }; - - useEffect(() => { - if (content === undefined) { - return; - } - bootstrapFile(fileName, fileType, content); - }, [fileName, fileType, content]); - - useImperativeHandle(ref, () => ({ - download: (name: string, type: FileTypes, body: string) => { - bootstrapFile(name, type, body); - anchorRef.current.click(); - }, - })); - - return {children}; - }, -); diff --git a/pkg/ui/src/components/downloadFile/index.ts b/pkg/ui/src/components/downloadFile/index.ts deleted file mode 100644 index 8b04ed62d58e..000000000000 --- a/pkg/ui/src/components/downloadFile/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -export * from "./downloadFile"; diff --git a/pkg/ui/src/components/index.ts b/pkg/ui/src/components/index.ts index 8d8514394c7c..1bb4c4ff3a94 100644 --- a/pkg/ui/src/components/index.ts +++ b/pkg/ui/src/components/index.ts @@ -18,10 +18,8 @@ export * from "./sideNavigation"; export * from "./pageHeader"; export * from "./text"; export * from "./input"; -export * from "./table"; export * from "./tooltip"; export * from "./select"; -export * from "./downloadFile"; export * from "./modal"; export * from "./rangeCalendar"; export * from "./link"; diff --git a/pkg/ui/src/components/table/index.ts b/pkg/ui/src/components/table/index.ts deleted file mode 100644 index cd7da8927732..000000000000 --- a/pkg/ui/src/components/table/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -export * from "./table"; diff --git a/pkg/ui/src/components/table/table.styl b/pkg/ui/src/components/table/table.styl deleted file mode 100644 index 30da384597eb..000000000000 --- a/pkg/ui/src/components/table/table.styl +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~src/components/core/index.styl' - -.crl-table-wrapper - .ant-table - color $colors--primary-text - - // Table header - .ant-table-thead - background-color $colors--neutral-1 - @extend $text--heading-6 - - .ant-table-thead > tr > th - padding $spacing-base $spacing-base - color $colors--neutral-6 - font-family $font-family--semi-bold - font-weight $font-weight--bold - letter-spacing 1.5px - .ant-table-column-sorter - vertical-align baseline - // END: Table header - - // Table Column - .column--align-right - text-align end - // END: Table Column - - // Expand/Collapse icon - .ant-table-row-expand-icon - border none - background-color transparent - - .ant-table-row-collapsed::after - content '▶' - font-size 8px - - .ant-table-row-expanded::after - content '▼' - font-size 8px - // END: Expand/Collapse icon - - // Table row - .ant-table-row - @extend $text--body - - .ant-table-row .cell--show-on-hover - visibility hidden - - .ant-table-row:hover .cell--show-on-hover - visibility visible - // END: Table row - - // Table cell - .ant-table-tbody > tr > td - padding $spacing-smaller $spacing-smaller - border-bottom-color $colors--neutral-3 - - // Increase right padding for columns aligned by right - .ant-table-tbody > tr > td.column--align-right - padding-right $spacing-mid-large - - // show column with right border - .ant-table-tbody > tr > td.column--border-right - border-right $colors--neutral-3 solid 1px - // END: Table cell - - // Table cell on hover - .ant-table-thead > tr.ant-table-row-hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td, - .ant-table-tbody > tr.ant-table-row-hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td, - .ant-table-thead > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td, - .ant-table-tbody > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td - background $colors--neutral-1 - // END: Table cell on hover - - .ant-table-placeholder - border $colors--neutral-1 solid 1px - - .empty-table__message - @extend $text--body - text-align center - - .ant-pagination.ant-table-pagination - text-align center - float unset - - &__empty - .ant-table-placeholder - border none diff --git a/pkg/ui/src/components/table/table.tsx b/pkg/ui/src/components/table/table.tsx deleted file mode 100644 index 39e27ea4bc09..000000000000 --- a/pkg/ui/src/components/table/table.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import * as React from "react"; -import { default as AntTable, ColumnProps } from "antd/es/table"; -import ConfigProvider from "antd/es/config-provider"; -import cn from "classnames"; - -import "antd/es/table/style/css"; -import "./table.styl"; - -export type ColumnsConfig = Array>; - -export interface TableProps { - columns: Array>; - dataSource: Array; - noDataMessage?: React.ReactNode; - tableLayout?: "fixed" | "auto"; - pageSize?: number; - className?: string; -} - -const customizeRenderEmpty = (node: React.ReactNode) => () => ( -
{node}
-); - -Table.defaultProps = { - noDataMessage: "No data to display", - tableLayout: "auto", - className: "", -}; - -export function Table(props: TableProps) { - const { - columns, - dataSource, - noDataMessage, - tableLayout, - pageSize, - className, - } = props; - return ( - - - className={cn(`crl-table-wrapper ${className}`, { - "crl-table-wrapper__empty": dataSource.length === 0, - })} - columns={columns} - dataSource={dataSource} - expandRowByClick - tableLayout={tableLayout} - pagination={{ hideOnSinglePage: true, pageSize }} - /> - - ); -} diff --git a/pkg/ui/src/redux/nodes.ts b/pkg/ui/src/redux/nodes.ts index 58adda225de4..8f69925cfa94 100644 --- a/pkg/ui/src/redux/nodes.ts +++ b/pkg/ui/src/redux/nodes.ts @@ -13,9 +13,9 @@ import { createSelector } from "reselect"; import * as protos from "src/js/protos"; import { AdminUIState } from "./state"; +import { util } from "@cockroachlabs/admin-ui-components"; import { Pick } from "src/util/pick"; import { NoConnection } from "src/views/reports/containers/network"; -import { INodeStatus, MetricConstants, BytesUsed } from "src/util/proto"; import { nullOfReturnType } from "src/util/types"; /** @@ -26,6 +26,9 @@ import { nullOfReturnType } from "src/util/types"; export import LivenessStatus = protos.cockroach.kv.kvserver.liveness.livenesspb.NodeLivenessStatus; import { cockroach } from "src/js/protos"; import MembershipStatus = cockroach.kv.kvserver.liveness.livenesspb.MembershipStatus; +import INodeStatus = cockroach.server.status.statuspb.INodeStatus; + +const { MetricConstants, BytesUsed } = util; /** * livenessNomenclature resolves a mismatch between the terms used for liveness diff --git a/pkg/ui/src/util/intersperse.spec.ts b/pkg/ui/src/util/intersperse.spec.ts deleted file mode 100644 index 7dd6f1a665ff..000000000000 --- a/pkg/ui/src/util/intersperse.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import { assert } from "chai"; - -import { intersperse } from "src/util/intersperse"; - -describe("intersperse", () => { - it("puts separator in between array items", () => { - const result = intersperse(["foo", "bar", "baz"], "-"); - assert.deepEqual(result, ["foo", "-", "bar", "-", "baz"]); - }); - - it("puts separator in between array items when given a one-item array", () => { - const result = intersperse(["baz"], "-"); - assert.deepEqual(result, ["baz"]); - }); - - it("puts separator in between array items when given an empty array", () => { - const result = intersperse([], "-"); - assert.deepEqual(result, []); - }); -}); diff --git a/pkg/ui/src/util/intersperse.ts b/pkg/ui/src/util/intersperse.ts deleted file mode 100644 index 41fb14553216..000000000000 --- a/pkg/ui/src/util/intersperse.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -// e.g. intersperse(["foo", "bar", "baz"], "-") => ["foo", "-", "bar", "-", "baz"] -export function intersperse(array: T[], sep: T): T[] { - const output = []; - for (let i = 0; i < array.length; i++) { - if (i > 0) { - output.push(sep); - } - output.push(array[i]); - } - return output; -} diff --git a/pkg/ui/src/views/cluster/containers/nodesOverview/index.tsx b/pkg/ui/src/views/cluster/containers/nodesOverview/index.tsx index 45ae5d3d715b..cd0748267dde 100644 --- a/pkg/ui/src/views/cluster/containers/nodesOverview/index.tsx +++ b/pkg/ui/src/views/cluster/containers/nodesOverview/index.tsx @@ -28,15 +28,8 @@ import { LocalSetting } from "src/redux/localsettings"; import { SortSetting } from "src/views/shared/components/sortabletable"; import { LongToMoment } from "src/util/convert"; import { INodeStatus, MetricConstants } from "src/util/proto"; -import { - ColumnsConfig, - Table, - Text, - TextTypes, - Tooltip, - Badge, - BadgeProps, -} from "src/components"; +import { Text, TextTypes, Tooltip, Badge, BadgeProps } from "src/components"; +import { ColumnsConfig, Table } from "@cockroachlabs/admin-ui-components"; import { Percentage } from "src/util/format"; import { FixLong } from "src/util/fixLong"; import { getNodeLocalityTiers } from "src/util/localities"; @@ -162,6 +155,7 @@ const getBadgeTypeByNodeStatus = ( } }; +// tslint:disable-next-line:variable-name const NodeNameColumn: React.FC<{ record: NodeStatusRow | DecommissionedNodeStatusRow; }> = ({ record }) => { @@ -173,6 +167,7 @@ const NodeNameColumn: React.FC<{ ); }; +// tslint:disable-next-line:variable-name const NodeLocalityColumn: React.FC<{ record: NodeStatusRow }> = ({ record, }) => { @@ -606,6 +601,7 @@ export const decommissionedNodesTableDataSelector = createSelector( /** * LiveNodesConnected is a redux-connected HOC of LiveNodeList. */ +// tslint:disable-next-line:variable-name const NodesConnected = connect( (state: AdminUIState) => { const liveNodes = partitionedStatuses(state).live || []; @@ -625,6 +621,7 @@ const NodesConnected = connect( /** * DecommissionedNodesConnected is a redux-connected HOC of NotLiveNodeList. */ +// tslint:disable-next-line:variable-name const DecommissionedNodesConnected = connect( (state: AdminUIState) => { return { diff --git a/pkg/ui/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx b/pkg/ui/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx index f71fee7c34de..5fcab5398d2d 100644 --- a/pkg/ui/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx +++ b/pkg/ui/src/views/reports/containers/nodeHistory/decommissionedNodeHistory.tsx @@ -24,7 +24,8 @@ import { SortSetting } from "src/views/shared/components/sortabletable"; import { LocalSetting } from "src/redux/localsettings"; import "./decommissionedNodeHistory.styl"; -import { ColumnsConfig, Table, Text } from "src/components"; +import { Text } from "src/components"; +import { ColumnsConfig, Table } from "@cockroachlabs/admin-ui-components"; import { createSelector } from "reselect"; const decommissionedNodesSortSetting = new LocalSetting< diff --git a/pkg/ui/src/views/reports/containers/statementDiagnosticsHistory/index.tsx b/pkg/ui/src/views/reports/containers/statementDiagnosticsHistory/index.tsx index 7af163f114ba..0dc6b89cf953 100644 --- a/pkg/ui/src/views/reports/containers/statementDiagnosticsHistory/index.tsx +++ b/pkg/ui/src/views/reports/containers/statementDiagnosticsHistory/index.tsx @@ -17,15 +17,7 @@ import Long from "long"; import { Link } from "react-router-dom"; import { isUndefined } from "lodash"; -import { - Anchor, - Button, - DownloadFile, - DownloadFileRef, - Text, - TextTypes, - Tooltip, -} from "src/components"; +import { Anchor, Button, Text, TextTypes, Tooltip } from "src/components"; import HeaderSection from "src/views/shared/components/headerSection"; import { AdminUIState } from "src/redux/state"; import { getStatementDiagnostics } from "src/util/api"; @@ -45,7 +37,6 @@ import "./statementDiagnosticsHistoryView.styl"; import { cockroach } from "src/js/protos"; import IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport; import StatementDiagnosticsRequest = cockroach.server.serverpb.StatementDiagnosticsRequest; -import { getDiagnosticsStatus } from "src/views/statements/diagnostics"; import { SortedTable, ColumnDescriptor, @@ -53,10 +44,15 @@ import { import { SortSetting } from "src/views/shared/components/sortabletable"; import { statementDiagnostics } from "src/util/docs"; import { summarize } from "src/util/sql/summarize"; -import { shortStatement } from "src/views/statements/statementsTable"; import { trackDownloadDiagnosticsBundle } from "src/util/analytics"; import EmptyTableIcon from "!!url-loader!assets/emptyState/empty-table-results.svg"; -import { EmptyTable } from "@cockroachlabs/admin-ui-components"; +import { + DownloadFile, + DownloadFileRef, + EmptyTable, + shortStatement, + getDiagnosticsStatus, +} from "@cockroachlabs/admin-ui-components"; type StatementDiagnosticsHistoryViewProps = MapStateToProps & MapDispatchToProps; diff --git a/pkg/ui/src/views/sessions/sessionDetails.tsx b/pkg/ui/src/views/sessions/sessionDetails.tsx index 8378cb23047c..9ffa59590b2f 100644 --- a/pkg/ui/src/views/sessions/sessionDetails.tsx +++ b/pkg/ui/src/views/sessions/sessionDetails.tsx @@ -36,7 +36,7 @@ import { nodeDisplayNameByIDSelector } from "src/redux/nodes"; import { NodeLink, StatementLinkTarget, -} from "src/views/statements/statementsTableContent"; +} from "@cockroachlabs/admin-ui-components"; import TerminateSessionModal, { TerminateSessionModalRef, } from "src/views/sessions/terminateSessionModal"; diff --git a/pkg/ui/src/views/sessions/sessionsTable.tsx b/pkg/ui/src/views/sessions/sessionsTable.tsx index f733ad89df22..e34e89bdbb25 100644 --- a/pkg/ui/src/views/sessions/sessionsTable.tsx +++ b/pkg/ui/src/views/sessions/sessionsTable.tsx @@ -23,7 +23,7 @@ import { Link } from "react-router-dom"; import React from "react"; import { Button, Tooltip } from "src/components"; import { Moment } from "moment"; -import { StatementLink } from "src/views/statements/statementsTableContent"; +import { StatementLink } from "@cockroachlabs/admin-ui-components"; import ISession = cockroach.server.serverpb.ISession; import { TerminateSessionModalRef } from "src/views/sessions/terminateSessionModal"; import { TerminateQueryModalRef } from "src/views/sessions/terminateQueryModal"; diff --git a/pkg/ui/src/views/statements/barCharts.module.styl b/pkg/ui/src/views/statements/barCharts.module.styl deleted file mode 100644 index a46266f08bfb..000000000000 --- a/pkg/ui/src/views/statements/barCharts.module.styl +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~styl/base/palette.styl' -@require '~src/components/core/index' - -.bar-chart - height 14px - position relative - display flex - align-items center - flex-direction row - > span - width 100% - display flex - align-items center - flex-direction row - - &__multiplebars - width calc(100% - 75px) - border-radius 3px - position relative - display flex - align-items center - - &__label - position relative - font-family SourceSansPro-Regular - font-size 12px - line-height 1.83 - color $adminui-grey-1 - width 75px - - &__bar - display inline-block - height 13px - border-radius 3px - - &--dev - position absolute - height 3px - - .count-first-try, .count-total, .count-retry, .count-max-retries - border-radius 3px - position absolute - left 40px - - .count-first-try, .count-total - background-color $grey-light - .count-retry, .count-max-retries - background-color $alert-color - - .rows - background-color $grey-light - border-radius 3px - - .rows-dev - background-color $colors--primary-blue-3 - - .bar-chart - &__parse, &__plan, &__run, &__overhead, &__overall - background-color $colors--neutral-4 - - &-red - .bar-chart - &__parse, &__plan, &__run, &__overhead, &__overall - background-color $colors--functional-red-2 - - &__parse-dev, &__plan-dev, &__run-dev, &__overhead-dev, &__overall-dev - background-color $colors--primary-blue-3 diff --git a/pkg/ui/src/views/statements/barCharts.stories.tsx b/pkg/ui/src/views/statements/barCharts.stories.tsx deleted file mode 100644 index ec705765f31b..000000000000 --- a/pkg/ui/src/views/statements/barCharts.stories.tsx +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { storiesOf, RenderFunction } from "@storybook/react"; - -import { - countBarChart, - genericBarChart, - latencyBarChart, - retryBarChart, - rowsBarChart, -} from "./barCharts"; -import statementsPagePropsFixture from "./statementsPage.fixture"; -import { cockroach } from "src/js/protos"; -import NumericStat = cockroach.sql.NumericStat; -import Long from "long"; - -const { statements } = statementsPagePropsFixture; - -const withinColumn = (width = "150px") => (storyFn: RenderFunction) => { - const rowStyle = { - borderTop: "1px solid #e7ecf3", - borderBottom: "1px solid #e7ecf3", - }; - - const cellStyle = { - width: "190px", - padding: "10px 20px", - }; - - return ( - - - - - - -
-
{storyFn()}
-
- ); -}; - -storiesOf("BarCharts", module) - .add("countBarChart", () => { - const chartFactory = countBarChart(statements); - return chartFactory(statements[0]); - }) - .add("latencyBarChart", () => { - const chartFactory = latencyBarChart(statements); - return chartFactory(statements[0]); - }) - .add("retryBarChart", () => { - const chartFactory = retryBarChart(statements); - return chartFactory(statements[0]); - }) - .add("rowsBarChart", () => { - const chartFactory = rowsBarChart(statements); - return chartFactory(statements[0]); - }) - .add("genericBarChart", () => { - const stat = new NumericStat(); - stat.mean = 25; - stat.squared_diffs = 25; - const barChartFactory = genericBarChart(stat, Long.fromInt(10)); - return barChartFactory(); - }) - .add("genericBarChart (missing data)", () => { - const barChartFactory = genericBarChart(null, Long.fromInt(10)); - return barChartFactory(); - }); - -storiesOf("BarCharts/within column (150px)", module) - .addDecorator(withinColumn()) - .add("countBarChart", () => { - const chartFactory = countBarChart(statements); - return chartFactory(statements[0]); - }) - .add("latencyBarChart", () => { - const chartFactory = latencyBarChart(statements); - return chartFactory(statements[0]); - }) - .add("retryBarChart", () => { - const chartFactory = retryBarChart(statements); - return chartFactory(statements[0]); - }) - .add("rowsBarChart", () => { - const chartFactory = rowsBarChart(statements); - return chartFactory(statements[0]); - }) - .add("genericBarChart", () => { - const stat = new NumericStat(); - stat.mean = 25; - stat.squared_diffs = 25; - const barChartFactory = genericBarChart(stat, Long.fromInt(10)); - return barChartFactory(); - }); diff --git a/pkg/ui/src/views/statements/barCharts.tsx b/pkg/ui/src/views/statements/barCharts.tsx deleted file mode 100644 index 1a8a6b724fb6..000000000000 --- a/pkg/ui/src/views/statements/barCharts.tsx +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import d3 from "d3"; -import _ from "lodash"; -import Long from "long"; -import React from "react"; -import * as protos from "src/js/protos"; -import { stdDevLong } from "src/util/appStats"; -import { FixLong } from "src/util/fixLong"; -import { Duration } from "src/util/format"; -import { ToolTipWrapper } from "src/views/shared/components/toolTip"; -import classNames from "classnames/bind"; -import styles from "./barCharts.module.styl"; - -type StatementStatistics = protos.cockroach.server.serverpb.StatementsResponse.ICollectedStatementStatistics; -type NumericStat = protos.cockroach.sql.INumericStat; - -const cx = classNames.bind(styles); - -interface BarChartOptions { - classes?: { - root?: string; - label?: string; - }; -} - -export const longToInt = (d: number | Long) => - Long.fromValue(FixLong(d)).toInt(); -const clamp = (i: number) => (i < 0 ? 0 : i); - -const formatTwoPlaces = d3.format(".2f"); - -const countBars = [ - bar("count-first-try", (d: StatementStatistics) => - longToInt(d.stats.first_attempt_count), - ), -]; - -const retryBars = [ - bar( - "count-retry", - (d: StatementStatistics) => - longToInt(d.stats.count) - longToInt(d.stats.first_attempt_count), - ), -]; - -const rowsBars = [ - bar("rows", (d: StatementStatistics) => d.stats.num_rows.mean), -]; - -const latencyBars = [ - bar("bar-chart__parse", (d: StatementStatistics) => d.stats.parse_lat.mean), - bar("bar-chart__plan", (d: StatementStatistics) => d.stats.plan_lat.mean), - bar("bar-chart__run", (d: StatementStatistics) => d.stats.run_lat.mean), - bar( - "bar-chart__overhead", - (d: StatementStatistics) => d.stats.overhead_lat.mean, - ), -]; - -const latencyStdDev = bar( - cx("bar-chart__overall-dev"), - (d: StatementStatistics) => stdDevLong(d.stats.service_lat, d.stats.count), -); -const rowsStdDev = bar(cx("rows-dev"), (d: StatementStatistics) => - stdDevLong(d.stats.num_rows, d.stats.count), -); - -function bar(name: string, value: (d: StatementStatistics) => number) { - return { name, value }; -} - -function renderNumericStatLegend( - count: number | Long, - stat: number, - sd: number, - formatter: (d: number) => string, -) { - return ( - - - - - - - - - - - -
-
- Mean -
{formatter(stat)}
-
- Standard Deviation -
{longToInt(count) < 2 ? "-" : sd ? formatter(sd) : "0"}
- ); -} - -const makeBarChart = ( - type: "grey" | "red", - accessors: { name: string; value: (d: StatementStatistics) => number }[], - formatter: (d: number) => string = (x) => `${x}`, - stdDevAccessor?: { name: string; value: (d: StatementStatistics) => number }, - legendFormatter?: (d: number) => string, -) => { - if (!legendFormatter) { - legendFormatter = formatter; - } - - return (rows: StatementStatistics[] = [], options: BarChartOptions = {}) => { - const getTotal = (d: StatementStatistics) => - _.sum(_.map(accessors, ({ value }) => value(d))); - const getTotalWithStdDev = (d: StatementStatistics) => - getTotal(d) + stdDevAccessor.value(d); - - const extent = d3.extent( - rows, - stdDevAccessor ? getTotalWithStdDev : getTotal, - ); - - const scale = d3.scale.linear().domain([0, extent[1]]).range([0, 100]); - - return (d: StatementStatistics) => { - if (rows.length === 0) { - scale.domain([0, getTotal(d)]); - } - - let sum = 0; - _.map(accessors, ({ name, value }) => { - const v = value(d); - sum += v; - return ( -
- ); - }); - - const renderStdDev = () => { - if (!stdDevAccessor) { - return null; - } - - const { name, value } = stdDevAccessor; - - const stddev = value(d); - const width = stddev + (stddev > sum ? sum : stddev); - const left = stddev > sum ? 0 : sum - stddev; - const cn = cx(name, "bar-chart__bar", "bar-chart__bar--dev"); - const style = { - width: scale(width) + "%", - left: scale(left) + "%", - }; - return
; - }; - - const className = cx("bar-chart", `bar-chart-${type}`, { - "bar-chart--singleton": rows.length === 0, - [options?.classes?.root]: !!options?.classes?.root, - }); - if (stdDevAccessor) { - const sd = stdDevAccessor.value(d); - const titleText = renderNumericStatLegend( - rows.length, - sum, - sd, - legendFormatter, - ); - return ( -
- -
- {formatter(getTotal(d))} -
-
-
- {renderStdDev()} -
- -
- ); - } else { - return ( -
-
- {formatter(getTotal(d))} -
-
-
- ); - } - }; - }; -}; - -const SCALE_FACTORS: { factor: number; key: string }[] = [ - { factor: 1000000000, key: "b" }, - { factor: 1000000, key: "m" }, - { factor: 1000, key: "k" }, -]; - -export function approximify(value: number) { - for (let i = 0; i < SCALE_FACTORS.length; i++) { - const scale = SCALE_FACTORS[i]; - if (value > scale.factor) { - return "" + Math.round(value / scale.factor) + scale.key; - } - } - - return "" + Math.round(value); -} - -export const countBarChart = makeBarChart("grey", countBars, approximify); -export const retryBarChart = makeBarChart("red", retryBars, approximify); -export const rowsBarChart = makeBarChart( - "grey", - rowsBars, - approximify, - rowsStdDev, - formatTwoPlaces, -); -export const latencyBarChart = makeBarChart( - "grey", - latencyBars, - (v) => Duration(v * 1e9), - latencyStdDev, -); - -export function rowsBreakdown(s: StatementStatistics) { - const mean = s.stats.num_rows.mean; - const sd = stdDevLong(s.stats.num_rows, s.stats.count); - - const scale = d3.scale - .linear() - .domain([0, mean + sd]) - .range([0, 100]); - - return { - rowsBarChart(meanRow?: boolean) { - const spread = scale(sd + (sd > mean ? mean : sd)); - if (meanRow) { - return formatTwoPlaces(mean); - } else { - return spread; - } - }, - }; -} - -export function genericBarChart( - s: NumericStat, - count: number | Long, - format?: (v: number) => string, -) { - if (!s) { - return () =>
; - } - const mean = s.mean; - const sd = stdDevLong(s, count); - - const max = mean + sd; - const scale = d3.scale.linear().domain([0, max]).range([0, 100]); - if (!format) { - format = d3.format(".2f"); - } - return function MakeGenericBarChart() { - const width = scale(clamp(mean - sd)); - const right = scale(mean); - const spread = scale(sd + (sd > mean ? mean : sd)); - const title = renderNumericStatLegend(count, mean, sd, format); - return ( - -
-
{format(mean)}
-
-
-
-
-
- - ); - }; -} - -export function latencyBreakdown(s: StatementStatistics) { - const parseMean = s.stats.parse_lat.mean; - const parseSd = stdDevLong(s.stats.parse_lat, s.stats.count); - - const planMean = s.stats.plan_lat.mean; - const planSd = stdDevLong(s.stats.plan_lat, s.stats.count); - - const runMean = s.stats.run_lat.mean; - const runSd = stdDevLong(s.stats.run_lat, s.stats.count); - - const overheadMean = s.stats.overhead_lat.mean; - const overheadSd = stdDevLong(s.stats.overhead_lat, s.stats.count); - - const overallMean = s.stats.service_lat.mean; - const overallSd = stdDevLong(s.stats.service_lat, s.stats.count); - - const max = Math.max( - parseMean + parseSd, - parseMean + planMean + planSd, - parseMean + planMean + runMean + runSd, - parseMean + planMean + runMean + overheadMean + overheadSd, - overallMean + overallSd, - ); - - const format = (v: number) => Duration(v * 1e9); - - const scale = d3.scale.linear().domain([0, max]).range([0, 100]); - - return { - parseBarChart() { - const width = scale(clamp(parseMean - parseSd)); - const right = scale(parseMean); - const spread = scale( - parseSd + (parseSd > parseMean ? parseMean : parseSd), - ); - const title = renderNumericStatLegend( - s.stats.count, - parseMean, - parseSd, - format, - ); - return ( - -
-
- {Duration(parseMean * 1e9)} -
-
-
-
-
-
- - ); - }, - - planBarChart() { - const left = scale(parseMean); - const width = scale(clamp(planMean - planSd)); - const right = scale(planMean); - const spread = scale(planSd + (planSd > planMean ? planMean : planSd)); - const title = renderNumericStatLegend( - s.stats.count, - planMean, - planSd, - format, - ); - return ( - -
-
- {Duration(planMean * 1e9)} -
-
-
-
-
-
- - ); - }, - - runBarChart() { - const left = scale(parseMean + planMean); - const width = scale(clamp(runMean - runSd)); - const right = scale(runMean); - const spread = scale(runSd + (runSd > runMean ? runMean : runSd)); - const title = renderNumericStatLegend( - s.stats.count, - runMean, - runSd, - format, - ); - return ( - -
-
- {Duration(runMean * 1e9)} -
-
-
-
-
-
- - ); - }, - - overheadBarChart() { - const left = scale(parseMean + planMean + runMean); - const width = scale(clamp(overheadMean - overheadSd)); - const right = scale(overheadMean); - const spread = scale( - overheadSd + (overheadSd > overheadMean ? overheadMean : overheadSd), - ); - const title = renderNumericStatLegend( - s.stats.count, - overheadMean, - overheadSd, - format, - ); - return ( - -
-
- {Duration(overheadMean * 1e9)} -
-
-
-
-
-
- - ); - }, - - overallBarChart() { - const parse = scale(parseMean); - const plan = scale(planMean); - const run = scale(runMean); - const overhead = scale(overheadMean); - const width = scale(clamp(overallMean - overallSd)); - const spread = scale( - overallSd + (overallSd > overallMean ? overallMean : overallSd), - ); - const title = renderNumericStatLegend( - s.stats.count, - overallMean, - overallSd, - format, - ); - return ( - -
-
- {Duration(overallMean * 1e9)} -
-
-
-
-
-
- - ); - }, - }; -} diff --git a/pkg/ui/src/views/statements/diagnostics/diagnosticsUtils.ts b/pkg/ui/src/views/statements/diagnostics/diagnosticsUtils.ts deleted file mode 100644 index eb824e9dee46..000000000000 --- a/pkg/ui/src/views/statements/diagnostics/diagnosticsUtils.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import { isUndefined } from "lodash"; - -import { cockroach } from "src/js/protos"; -import IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport; -import { DiagnosticStatuses } from "src/views/statements/diagnostics/diagnosticStatuses"; - -export function getDiagnosticsStatus( - diagnosticsRequest: IStatementDiagnosticsReport, -): DiagnosticStatuses { - if (diagnosticsRequest.completed) { - return "READY"; - } - - return "WAITING"; -} - -export function sortByRequestedAtField( - a: IStatementDiagnosticsReport, - b: IStatementDiagnosticsReport, -) { - const activatedOnA = a.requested_at?.seconds?.toNumber(); - const activatedOnB = b.requested_at?.seconds?.toNumber(); - if (isUndefined(activatedOnA) && isUndefined(activatedOnB)) { - return 0; - } - if (activatedOnA < activatedOnB) { - return -1; - } - if (activatedOnA > activatedOnB) { - return 1; - } - return 0; -} - -export function sortByCompletedField( - a: IStatementDiagnosticsReport, - b: IStatementDiagnosticsReport, -) { - const completedA = a.completed ? 1 : -1; - const completedB = b.completed ? 1 : -1; - if (completedA < completedB) { - return -1; - } - if (completedA > completedB) { - return 1; - } - return 0; -} - -export function sortByStatementFingerprintField( - a: IStatementDiagnosticsReport, - b: IStatementDiagnosticsReport, -) { - const statementFingerprintA = a.statement_fingerprint; - const statementFingerprintB = b.statement_fingerprint; - if ( - isUndefined(statementFingerprintA) && - isUndefined(statementFingerprintB) - ) { - return 0; - } - if (statementFingerprintA < statementFingerprintB) { - return -1; - } - if (statementFingerprintA > statementFingerprintB) { - return 1; - } - return 0; -} diff --git a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.module.styl b/pkg/ui/src/views/statements/diagnostics/diagnosticsView.module.styl deleted file mode 100644 index 14d7f4c19d46..000000000000 --- a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.module.styl +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~src/components/core/index.styl' - -.crl-statements-diagnostics-view - display flex - flex-direction column - - &__title - display flex - flex-direction row - justify-content space-between - margin-bottom $spacing-mid-large - - &__footer - margin $spacing-medium 0 0 $spacing-smaller - - &__content - max-width 650px - - &__main - margin-bottom $spacing-small - color $colors--secondary-text - - &__actions-column - display flex - flex-direction row - flex-wrap nowrap - justify-content flex-end - - &__vertical-line - width 1px - min-height 100% - border-left 1px solid $colors--neutral-3 - margin 0 $spacing-x-small - - &__icon - display inline-block - fill inherit - font-style normal - line-height 0 - text-align center - text-transform none - vertical-align -0.125em - margin-right $spacing-x-small - - &__statements-link - color $colors--primary-text - &:hover - color $colors--link - -.summary--card__empty-state - background-color $colors--white - background-image url("../../../../assets/statementsPage/emptyTracingBackground.svg") - background-repeat no-repeat - background-position-x right - padding 0 - diff --git a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.spec.tsx b/pkg/ui/src/views/statements/diagnostics/diagnosticsView.spec.tsx deleted file mode 100644 index 50964796da2b..000000000000 --- a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.spec.tsx +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { assert } from "chai"; -import { mount, ReactWrapper } from "enzyme"; -import sinon, { SinonSpy } from "sinon"; -import Long from "long"; -import classNames from "classnames/bind"; -import { MemoryRouter } from "react-router-dom"; - -import "src/enzymeInit"; -import { DiagnosticsView } from "./diagnosticsView"; -import { Table } from "src/components"; -import { connectedMount } from "src/test-utils"; -import { cockroach } from "src/js/protos"; -import IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport; -import buttonStyles from "src/components/button/button.module.styl"; - -const cx = classNames.bind(buttonStyles); -const sandbox = sinon.createSandbox(); - -describe("DiagnosticsView", () => { - let wrapper: ReactWrapper; - let activateFn: SinonSpy; - const statementFingerprint = "some-id"; - - beforeEach(() => { - sandbox.reset(); - activateFn = sandbox.spy(); - }); - - describe("With Empty state", () => { - beforeEach(() => { - wrapper = mount( - - {}} - /> - , - ); - }); - - it("calls activate callback with statementId when click on Activate button", () => { - const activateButtonComponent = wrapper - .find(`.${cx("crl-button")}`) - .first(); - activateButtonComponent.simulate("click"); - activateFn.calledOnceWith(statementFingerprint); - }); - }); - - describe("With tracing data", () => { - beforeEach(() => { - const diagnosticsRequests: IStatementDiagnosticsReport[] = [ - generateDiagnosticsRequest(), - generateDiagnosticsRequest(), - ]; - - wrapper = connectedMount(() => ( - {}} - /> - )); - }); - - it("renders Table component when diagnostics data is provided", () => { - assert.isTrue(wrapper.find(Table).exists()); - }); - - it("calls activate callback with statementId when click on Activate button", () => { - const activateButtonComponent = wrapper - .find(`.${cx("crl-button")}`) - .first(); - activateButtonComponent.simulate("click"); - activateFn.calledOnceWith(statementFingerprint); - }); - - it("Activate button is hidden if diagnostics is requested and waiting query", () => { - const diagnosticsRequests: IStatementDiagnosticsReport[] = [ - generateDiagnosticsRequest({ completed: false }), - generateDiagnosticsRequest(), - ]; - wrapper = connectedMount(() => ( - {}} - /> - )); - - const activateButtonComponent = wrapper - .find(".crl-statements-diagnostics-view__activate-button") - .first(); - assert.isFalse(activateButtonComponent.exists()); - }); - }); -}); - -function generateDiagnosticsRequest( - extendObject: Partial = {}, -): IStatementDiagnosticsReport { - const diagnosticsRequest = { - statement_fingerprint: "SELECT * FROM table", - completed: true, - requested_at: { - seconds: Long.fromNumber(Date.now()), - nanos: Math.random() * 1000000, - }, - }; - Object.assign(diagnosticsRequest, extendObject); - return diagnosticsRequest; -} diff --git a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.tsx b/pkg/ui/src/views/statements/diagnostics/diagnosticsView.tsx deleted file mode 100644 index c052a3e544bb..000000000000 --- a/pkg/ui/src/views/statements/diagnostics/diagnosticsView.tsx +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { connect } from "react-redux"; -import moment from "moment"; -import Long from "long"; -import classnames from "classnames/bind"; - -import { - Button, - Text, - TextTypes, - Table, - ColumnsConfig, - DownloadFile, - DownloadFileRef, - Anchor, - Link, -} from "src/components"; -import { AdminUIState } from "src/redux/state"; -import { getStatementDiagnostics } from "src/util/api"; -import { SummaryCard } from "src/views/shared/components/summaryCard"; -import { - selectDiagnosticsReportsByStatementFingerprint, - selectDiagnosticsReportsCountByStatementFingerprint, -} from "src/redux/statements/statementsSelectors"; -import { createStatementDiagnosticsReportAction } from "src/redux/statements"; -import { DiagnosticStatusBadge } from "./diagnosticStatusBadge"; -import EmptyListIcon from "!!url-loader!assets/emptyState/empty-list-results.svg"; -import styles from "./diagnosticsView.module.styl"; -import { cockroach } from "src/js/protos"; -import IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport; -import StatementDiagnosticsRequest = cockroach.server.serverpb.StatementDiagnosticsRequest; -import { - getDiagnosticsStatus, - sortByCompletedField, - sortByRequestedAtField, -} from "./diagnosticsUtils"; -import { statementDiagnostics } from "src/util/docs"; -import { createStatementDiagnosticsAlertLocalSetting } from "src/redux/alerts"; -import { - trackActivateDiagnostics, - trackDownloadDiagnosticsBundle, -} from "src/util/analytics"; -import { EmptyTable } from "@cockroachlabs/admin-ui-components"; -import { Download } from "@cockroachlabs/icons"; - -interface DiagnosticsViewOwnProps { - statementFingerprint?: string; -} - -type DiagnosticsViewProps = DiagnosticsViewOwnProps & - MapStateToProps & - MapDispatchToProps; - -interface DiagnosticsViewState { - traces: { - [diagnosticsId: string]: string; - }; -} - -const cx = classnames.bind(styles); - -export class DiagnosticsView extends React.Component< - DiagnosticsViewProps, - DiagnosticsViewState -> { - columns: ColumnsConfig = [ - { - key: "activatedOn", - title: "Activated on", - sorter: sortByRequestedAtField, - defaultSortOrder: "descend", - render: (_text, record) => { - const timestamp = record.requested_at.seconds.toNumber() * 1000; - return moment(timestamp).format("LL[ at ]h:mm a"); - }, - }, - { - key: "status", - title: "status", - sorter: sortByCompletedField, - width: "160px", - render: (_text, record) => { - const status = getDiagnosticsStatus(record); - return ( - - ); - }, - }, - { - key: "actions", - title: "", - sorter: false, - width: "160px", - render: (_text, record) => { - if (record.completed) { - return ( -
- - trackDownloadDiagnosticsBundle(record.statement_fingerprint) - } - > - - -
- ); - } - return null; - }, - }, - ]; - - downloadRef = React.createRef(); - - getStatementDiagnostics = async (diagnosticsId: Long) => { - const request = new StatementDiagnosticsRequest({ - statement_diagnostics_id: diagnosticsId, - }); - const response = await getStatementDiagnostics(request); - const trace = response.diagnostics?.trace; - this.downloadRef.current?.download( - "statement-diagnostics.json", - "application/json", - trace, - ); - }; - - onActivateButtonClick = () => { - const { activate, statementFingerprint } = this.props; - activate(statementFingerprint); - trackActivateDiagnostics(statementFingerprint); - }; - - componentWillUnmount() { - this.props.dismissAlertMessage(); - } - - render() { - const { diagnosticsReports } = this.props; - - const canRequestDiagnostics = diagnosticsReports.every( - (diagnostic) => diagnostic.completed, - ); - - const dataSource = diagnosticsReports.map((diagnosticsReport, idx) => ({ - ...diagnosticsReport, - key: idx, - })); - - return ( - -
- Statement diagnostics - {canRequestDiagnostics && ( - - )} -
- - - {"When you activate statement diagnostics, CockroachDB will wait for the next query that" + - " matches this statement fingerprint. A download button will appear on the statement list and" + - " detail pages when the query is ready. The statement diagnostic will include EXPLAIN plans, table" + - " statistics, and traces. "} - - - Learn More - - - } - footer={ - - } - /> - } - dataSource={dataSource} - columns={this.columns} - /> -
- - All statement diagnostics - -
- - - ); - } -} - -interface MapStateToProps { - hasData: boolean; - diagnosticsReports: IStatementDiagnosticsReport[]; -} - -interface MapDispatchToProps { - activate: (statementFingerprint: string) => void; - dismissAlertMessage: () => void; -} - -const mapStateToProps = ( - state: AdminUIState, - props: DiagnosticsViewProps, -): MapStateToProps => { - const { statementFingerprint } = props; - const hasData = - selectDiagnosticsReportsCountByStatementFingerprint( - state, - statementFingerprint, - ) > 0; - const diagnosticsReports = selectDiagnosticsReportsByStatementFingerprint( - state, - statementFingerprint, - ); - return { - hasData, - diagnosticsReports, - }; -}; - -const mapDispatchToProps: MapDispatchToProps = { - activate: createStatementDiagnosticsReportAction, - dismissAlertMessage: () => - createStatementDiagnosticsAlertLocalSetting.set({ show: false }), -}; - -export default connect< - MapStateToProps, - MapDispatchToProps, - DiagnosticsViewOwnProps ->( - mapStateToProps, - mapDispatchToProps, -)(DiagnosticsView); diff --git a/pkg/ui/src/views/statements/diagnostics/index.ts b/pkg/ui/src/views/statements/diagnostics/index.ts index d3f8b9f159eb..7b0ee512512e 100644 --- a/pkg/ui/src/views/statements/diagnostics/index.ts +++ b/pkg/ui/src/views/statements/diagnostics/index.ts @@ -8,7 +8,5 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -import DiagnosticsView from "./diagnosticsView"; - -export default DiagnosticsView; -export * from "./diagnosticsUtils"; +export * from "./activateDiagnosticsModal"; +export * from "./diagnosticStatusBadge"; diff --git a/pkg/ui/src/views/statements/planView/index.ts b/pkg/ui/src/views/statements/planView/index.ts deleted file mode 100644 index ecd89e159553..000000000000 --- a/pkg/ui/src/views/statements/planView/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -export * from "./planView"; diff --git a/pkg/ui/src/views/statements/planView/planView.fixtures.tsx b/pkg/ui/src/views/statements/planView/planView.fixtures.tsx deleted file mode 100644 index 2ace0ae87db1..000000000000 --- a/pkg/ui/src/views/statements/planView/planView.fixtures.tsx +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import { cockroach } from "src/js/protos"; -import IExplainTreePlanNode = cockroach.sql.IExplainTreePlanNode; - -export const logicalPlan: IExplainTreePlanNode = { - name: "root", - attrs: [], - children: [ - { - name: "count", - attrs: [], - children: [ - { - name: "upsert", - attrs: [ - { - key: "into", - value: - "vehicle_location_histories(city, ride_id, timestamp, lat, long)", - }, - { - key: "strategy", - value: "opt upserter", - }, - ], - children: [ - { - name: "buffer node", - attrs: [ - { - key: "label", - value: "buffer 1", - }, - ], - children: [ - { - name: "row source to plan node", - attrs: [], - children: [ - { - name: "render", - attrs: [ - { - key: "render", - value: "column1", - }, - { - key: "render", - value: "column2", - }, - { - key: "render", - value: "column3", - }, - { - key: "render", - value: "column4", - }, - { - key: "render", - value: "column5", - }, - { - key: "render", - value: "column4", - }, - { - key: "render", - value: "column5", - }, - ], - children: [ - { - name: "values", - attrs: [ - { - key: "size", - value: "5 columns, 1 row", - }, - { - key: "row 0, expr", - value: "_", - }, - { - key: "row 0, expr", - value: "_", - }, - { - key: "row 0, expr", - value: "now()", - }, - { - key: "row 0, expr", - value: "_", - }, - { - key: "row 0, expr", - value: "_", - }, - ], - children: [], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - { - name: "postquery", - attrs: [], - children: [ - { - name: "error if rows", - attrs: [], - children: [ - { - name: "row source to plan node", - attrs: [], - children: [ - { - name: "lookup-join", - attrs: [ - { - key: "table", - value: "rides@primary", - }, - { - key: "type", - value: "anti", - }, - { - key: "equality", - value: "(column1, column2) = (city, id)", - }, - { - key: "equality cols are key", - value: "", - }, - { - key: "parallel", - value: "", - }, - ], - children: [ - { - name: "render", - attrs: [ - { - key: "render", - value: "column1", - }, - { - key: "render", - value: "column2", - }, - ], - children: [ - { - name: "scan buffer node", - children: [], - attrs: [ - { - key: "label", - value: "buffer 1", - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], -}; diff --git a/pkg/ui/src/views/statements/planView/planView.module.styl b/pkg/ui/src/views/statements/planView/planView.module.styl deleted file mode 100644 index f7d4a2ab51ff..000000000000 --- a/pkg/ui/src/views/statements/planView/planView.module.styl +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~styl/base/palette.styl' -@require '~src/views/shared/util/table.styl' -@require '~src/components/core/index' - -.base-heading - composes base-heading from '~styl/base/typography.styl' - -.plan-view-table - @extend $table-base - .plan-view-table__cell - padding 0 - .summary--card__title - font-family SourceSansPro-Regular - line-height 1.6 - letter-spacing -0.2px - color $popover-color - font-size 16px - display inline-block - margin-bottom 10px - padding 0 - text-transform none - &__row - &--body - border-top none - &:hover - background-color $adminui-white - &__tooltip - .hover-tooltip__text - width 520px - margin-left 15px - -.plan-view-table - &__tooltip - width 36px // Reserve space for 10px padding around centered 16px icon - height 16px - display inline-block - - // Overrides to let the tooltip sit inside a table header. - text-transform none - font-weight normal - white-space normal - letter-spacing normal - font-size 14px - - &__tooltip-hover-area - width 100% - padding 0px 10px - - &__info-icon - width 16px - height 16px - border-radius 50% - border 1px solid $tooltip-color - font-size 12px - line-height 14px // Visual tweak to vertically center the "i" - text-align center - color $tooltip-color - - .hover-tooltip--hovered &__info-icon - border-color $body-color - color $body-color - -.plan-view - color $body-color - position relative - - .plan-view-container - height 100% - max-height 100% - overflow hidden - - .plan-view-container-scroll - max-height 400px - overflow-y scroll - - .plan-view-container-directions - text-align center - cursor pointer - text-transform uppercase - color $main-blue-color - font-size smaller - - .node-icon - margin 0 10px 0 0 - color $grey-light - .warning-icon - margin 0 4px 0 4px - position relative - top 3px - path - fill $colors--functional-orange-4 - - .warn - position relative - left -5px - color $colors--functional-orange-4 - background-color $plan-node-warning-background-color - border-radius 2px - padding 2px - - .nodeDetails - position relative - padding 6px 0 - border 1px solid transparent - b - font-family SourceSansPro-SemiBold - font-size 12px - font-weight 600 - line-height 1.67 - letter-spacing 0.3px - color $text-color - - .nodeAttributes - color $adminui-grey-2 - padding 7px 16px 0px 18px - margin-left 3px - border-left 1px solid $grey-light - font-family RobotoMono-Medium - font-size 12px - font-weight 500 - line-height 1.83 - - .nodeAttributeKey - color $colors--primary-green-3 - - ul - padding 0 - margin 0 - li - padding 0 - margin 0 - position relative - list-style-type none - - // vertical line, to previous node (above) - &:not(:first-child):after - content '' - width 1px - height 19px - background-color $grey-light - position absolute - top -10px - left 4px - - ul - padding-left 27px - position relative - &:last-child - &:before - content '' - width 28px - height 29px - position absolute - border-left 1px solid $grey-light - border-bottom 1px solid $grey-light - top -10px - left 4px - border-bottom-left-radius 10px - li - &:before - content none - &:first-child:after - content none - li - // first node: horizontal line, to parent - .nodeDetails - margin-left 12px - &:not(:first-child):after - left 16px - &:last-child - .nodeAttributes - border-color transparent - &:first-child - &:after - content '' - height 1px - width 27px - background-color $grey-light - position absolute - top 18px - left -22px - &:before - content '' - width 1px - height 100% - background-color $grey-light - position absolute - top -10px - left -23px diff --git a/pkg/ui/src/views/statements/planView/planView.spec.tsx b/pkg/ui/src/views/statements/planView/planView.spec.tsx deleted file mode 100644 index b4cfc6f81121..000000000000 --- a/pkg/ui/src/views/statements/planView/planView.spec.tsx +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import { assert } from "chai"; - -import { cockroach } from "src/js/protos"; -import { - FlatPlanNode, - FlatPlanNodeAttribute, - flattenTree, - flattenAttributes, -} from "src/views/statements/planView"; -import IAttr = cockroach.sql.ExplainTreePlanNode.IAttr; -import IExplainTreePlanNode = cockroach.sql.IExplainTreePlanNode; - -const testAttrs1: IAttr[] = [ - { - key: "key1", - value: "value1", - }, - { - key: "key2", - value: "value2", - }, -]; - -const testAttrs2: IAttr[] = [ - { - key: "key3", - value: "value3", - }, - { - key: "key4", - value: "value4", - }, -]; - -const testFlatAttrs1: FlatPlanNodeAttribute[] = [ - { - key: "key1", - values: ["value1"], - warn: false, - }, - { - key: "key2", - values: ["value2"], - warn: false, - }, -]; - -const testFlatAttrs2: FlatPlanNodeAttribute[] = [ - { - key: "key3", - values: ["value3"], - warn: false, - }, - { - key: "key4", - values: ["value4"], - warn: false, - }, -]; - -const treePlanWithSingleChildPaths: IExplainTreePlanNode = { - name: "root", - attrs: null, - children: [ - { - name: "single_grandparent", - attrs: testAttrs1, - children: [ - { - name: "single_parent", - attrs: null, - children: [ - { - name: "single_child", - attrs: testAttrs2, - children: [], - }, - ], - }, - ], - }, - ], -}; - -const expectedFlatPlanWithSingleChildPaths: FlatPlanNode[] = [ - { - name: "root", - attrs: [], - children: [], - }, - { - name: "single_grandparent", - attrs: testFlatAttrs1, - children: [], - }, - { - name: "single_parent", - attrs: [], - children: [], - }, - { - name: "single_child", - attrs: testFlatAttrs2, - children: [], - }, -]; - -const treePlanWithChildren1: IExplainTreePlanNode = { - name: "root", - attrs: testAttrs1, - children: [ - { - name: "single_grandparent", - attrs: testAttrs1, - children: [ - { - name: "parent_1", - attrs: null, - children: [ - { - name: "single_child", - attrs: testAttrs2, - children: [], - }, - ], - }, - { - name: "parent_2", - attrs: null, - children: [], - }, - ], - }, - ], -}; - -const expectedFlatPlanWithChildren1: FlatPlanNode[] = [ - { - name: "root", - attrs: testFlatAttrs1, - children: [], - }, - { - name: "single_grandparent", - attrs: testFlatAttrs1, - children: [ - [ - { - name: "parent_1", - attrs: [], - children: [], - }, - { - name: "single_child", - attrs: testFlatAttrs2, - children: [], - }, - ], - [ - { - name: "parent_2", - attrs: [], - children: [], - }, - ], - ], - }, -]; - -const treePlanWithChildren2: IExplainTreePlanNode = { - name: "root", - attrs: null, - children: [ - { - name: "single_grandparent", - attrs: null, - children: [ - { - name: "single_parent", - attrs: null, - children: [ - { - name: "child_1", - attrs: testAttrs1, - children: [], - }, - { - name: "child_2", - attrs: testAttrs2, - children: [], - }, - ], - }, - ], - }, - ], -}; - -const expectedFlatPlanWithChildren2: FlatPlanNode[] = [ - { - name: "root", - attrs: [], - children: [], - }, - { - name: "single_grandparent", - attrs: [], - children: [], - }, - { - name: "single_parent", - attrs: [], - children: [ - [ - { - name: "child_1", - attrs: testFlatAttrs1, - children: [], - }, - ], - [ - { - name: "child_2", - attrs: testFlatAttrs2, - children: [], - }, - ], - ], - }, -]; - -const treePlanWithNoChildren: IExplainTreePlanNode = { - name: "root", - attrs: testAttrs1, - children: [], -}; - -const expectedFlatPlanWithNoChildren: FlatPlanNode[] = [ - { - name: "root", - attrs: testFlatAttrs1, - children: [], - }, -]; - -describe("flattenTree", () => { - describe("when node has children", () => { - it("flattens single child paths.", () => { - assert.deepEqual( - flattenTree(treePlanWithSingleChildPaths), - expectedFlatPlanWithSingleChildPaths, - ); - }); - it("increases level if multiple children.", () => { - assert.deepEqual( - flattenTree(treePlanWithChildren1), - expectedFlatPlanWithChildren1, - ); - assert.deepEqual( - flattenTree(treePlanWithChildren2), - expectedFlatPlanWithChildren2, - ); - }); - }); - describe("when node has no children", () => { - it("returns valid flattened plan.", () => { - assert.deepEqual( - flattenTree(treePlanWithNoChildren), - expectedFlatPlanWithNoChildren, - ); - }); - }); -}); - -describe("flattenAttributes", () => { - describe("when all attributes have different keys", () => { - it("creates array with exactly one value for each attribute", () => { - const testAttrs: IAttr[] = [ - { - key: "key1", - value: "value1", - }, - { - key: "key2", - value: "value2", - }, - ]; - const expectedTestAttrs: FlatPlanNodeAttribute[] = [ - { - key: "key1", - values: ["value1"], - warn: false, - }, - { - key: "key2", - values: ["value2"], - warn: false, - }, - ]; - - assert.deepEqual(flattenAttributes(testAttrs), expectedTestAttrs); - }); - }); - describe("when there are multiple attributes with same key", () => { - it("collects values into one array for same key", () => { - const testAttrs: IAttr[] = [ - { - key: "key1", - value: "key1-value1", - }, - { - key: "key2", - value: "key2-value1", - }, - { - key: "key1", - value: "key1-value2", - }, - ]; - const expectedTestAttrs: FlatPlanNodeAttribute[] = [ - { - key: "key1", - values: ["key1-value1", "key1-value2"], - warn: false, - }, - { - key: "key2", - values: ["key2-value1"], - warn: false, - }, - ]; - - assert.deepEqual(flattenAttributes(testAttrs), expectedTestAttrs); - }); - }); - describe("when attribute key/value is `spans FULL SCAN`", () => { - it("sets warn to true", () => { - const testAttrs: IAttr[] = [ - { - key: "foo", - value: "bar", - }, - { - key: "spans", - value: "FULL SCAN", - }, - ]; - const expectedTestAttrs: FlatPlanNodeAttribute[] = [ - { - key: "foo", - values: ["bar"], - warn: false, - }, - { - key: "spans", - values: ["FULL SCAN"], - warn: true, - }, - ]; - - assert.deepEqual(flattenAttributes(testAttrs), expectedTestAttrs); - }); - }); - describe("when keys are unsorted", () => { - it("puts table key first, and sorts remaining keys alphabetically", () => { - const testAttrs: IAttr[] = [ - { - key: "zebra", - value: "foo", - }, - { - key: "table", - value: "foo", - }, - { - key: "cheetah", - value: "foo", - }, - { - key: "table", - value: "bar", - }, - ]; - const expectedTestAttrs: FlatPlanNodeAttribute[] = [ - { - key: "table", - values: ["foo", "bar"], - warn: false, - }, - { - key: "cheetah", - values: ["foo"], - warn: false, - }, - { - key: "zebra", - values: ["foo"], - warn: false, - }, - ]; - - assert.deepEqual(flattenAttributes(testAttrs), expectedTestAttrs); - }); - }); -}); diff --git a/pkg/ui/src/views/statements/planView/planView.stories.tsx b/pkg/ui/src/views/statements/planView/planView.stories.tsx deleted file mode 100644 index 7a832a1fedcc..000000000000 --- a/pkg/ui/src/views/statements/planView/planView.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { storiesOf } from "@storybook/react"; -import { PlanView } from "./planView"; -import { logicalPlan } from "./planView.fixtures"; - -storiesOf("PlanView", module).add("default", () => ( - -)); diff --git a/pkg/ui/src/views/statements/planView/planView.tsx b/pkg/ui/src/views/statements/planView/planView.tsx deleted file mode 100644 index 436956619be0..000000000000 --- a/pkg/ui/src/views/statements/planView/planView.tsx +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import _ from "lodash"; -import React, { Fragment } from "react"; -import classNames from "classnames/bind"; -import { cockroach } from "src/js/protos"; -import { ToolTipWrapper } from "src/views/shared/components/toolTip"; -import styles from "./planView.module.styl"; - -import IAttr = cockroach.sql.ExplainTreePlanNode.IAttr; -import IExplainTreePlanNode = cockroach.sql.IExplainTreePlanNode; - -const cx = classNames.bind(styles); - -const WARNING_ICON = ( - - - -); -const NODE_ICON = ; - -// FlatPlanNodeAttribute contains a flattened representation of IAttr[]. -export interface FlatPlanNodeAttribute { - key: string; - values: string[]; - warn: boolean; -} - -// FlatPlanNode contains details for the flattened representation of -// IExplainTreePlanNode. -// -// Note that the function that flattens IExplainTreePlanNode returns -// an array of FlatPlanNode (not a single FlatPlanNode). E.g.: -// -// flattenTree(IExplainTreePlanNode) => FlatPlanNode[] -// -export interface FlatPlanNode { - name: string; - attrs: FlatPlanNodeAttribute[]; - children: FlatPlanNode[][]; -} - -/* ************************* HELPER FUNCTIONS ************************* */ - -// flattenTree takes a tree representation of a logical plan -// (IExplainTreePlanNode) and flattens any single child paths. -// For example, if an IExplainTreePlanNode was visually displayed -// as: -// -// root -// | -// |___ single_grandparent -// | -// |____ parent_1 -// | | -// | |______ single_child -// | -// |____ parent_2 -// -// Then its FlatPlanNode[] equivalent would be visually displayed -// as: -// -// root -// | -// single_grandparent -// | -// |____ parent_1 -// | | -// | single_child -// | -// |____ parent_2 -// -export function flattenTree(treePlan: IExplainTreePlanNode): FlatPlanNode[] { - const flattenedPlan: FlatPlanNode[] = [ - { - name: treePlan.name, - attrs: flattenAttributes(treePlan.attrs), - children: [], - }, - ]; - - if (treePlan.children.length === 0) { - return flattenedPlan; - } - const flattenedChildren = treePlan.children.map((child) => - flattenTree(child), - ); - if (treePlan.children.length === 1) { - // Append single child into same list that contains parent node. - flattenedPlan.push(...flattenedChildren[0]); - } else { - // Only add to children property if there are multiple children. - flattenedPlan[0].children = flattenedChildren; - } - return flattenedPlan; -} - -// flattenAttributes takes a list of attrs (IAttr[]) and collapses -// all the values for the same key (FlatPlanNodeAttribute). For example, -// if attrs was: -// -// attrs: IAttr[] = [ -// { -// key: "render", -// value: "name", -// }, -// { -// key: "render", -// value: "title", -// }, -// ]; -// -// The returned FlatPlanNodeAttribute would be: -// -// flattenedAttr: FlatPlanNodeAttribute = { -// key: "render", -// value: ["name", "title"], -// }; -// -export function flattenAttributes( - attrs: IAttr[] | null, -): FlatPlanNodeAttribute[] { - if (attrs === null) { - return []; - } - const flattenedAttrsMap: { [key: string]: FlatPlanNodeAttribute } = {}; - attrs.forEach((attr) => { - const existingAttr = flattenedAttrsMap[attr.key]; - const warn = warnForAttribute(attr); - if (!existingAttr) { - flattenedAttrsMap[attr.key] = { - key: attr.key, - values: [attr.value], - warn: warn, - }; - } else { - existingAttr.values.push(attr.value); - if (warn) { - existingAttr.warn = true; - } - } - }); - const flattenedAttrs = _.values(flattenedAttrsMap); - return _.sortBy(flattenedAttrs, (attr) => - attr.key === "table" ? "table" : "z" + attr.key, - ); -} - -function warnForAttribute(attr: IAttr): boolean { - // TODO(yuzefovich): 'spans ALL' is pre-20.1 attribute (and it might show up - // during an upgrade), so we should remove the check for it after 20.2 - // release. - if ( - attr.key === "spans" && - (attr.value === "FULL SCAN" || attr.value === "ALL") - ) { - return true; - } - return false; -} - -// shouldHideNode looks at node name to determine whether we should hide -// node from logical plan tree. -// -// Currently we're hiding `row source to planNode`, which is a node -// generated during execution (e.g. this is an internal implementation -// detail that will add more confusion than help to user). See #34594 -// for details. -function shouldHideNode(nodeName: string): boolean { - if (nodeName === "row source to plan node") { - return true; - } - return false; -} - -/* ************************* PLAN NODES ************************* */ - -interface PlanNodeDetailProps { - node: FlatPlanNode; -} - -class PlanNodeDetails extends React.Component { - constructor(props: PlanNodeDetailProps) { - super(props); - } - - renderAttributeValues(values: string[]) { - if (!values.length || !values[0].length) { - return; - } - if (values.length === 1) { - return = {values[0]}; - } - return = [{values.join(", ")}]; - } - - renderAttribute(attr: FlatPlanNodeAttribute) { - let attrClassName = ""; - let keyClassName = "nodeAttributeKey"; - if (attr.warn) { - attrClassName = "warn"; - keyClassName = ""; - } - return ( -
- {attr.warn && WARNING_ICON} - {attr.key} - {this.renderAttributeValues(attr.values)} -
- ); - } - - renderNodeDetails() { - const node = this.props.node; - if (node.attrs && node.attrs.length > 0) { - return ( -
- {node.attrs.map((attr) => this.renderAttribute(attr))} -
- ); - } - } - - render() { - const node = this.props.node; - return ( -
- {NODE_ICON} {_.capitalize(node.name)} - {this.renderNodeDetails()} -
- ); - } -} - -function PlanNodes(props: { nodes: FlatPlanNode[] }): React.ReactElement<{}> { - const nodes = props.nodes; - return ( -
    - {nodes.map((node) => { - return ; - })} -
- ); -} - -interface PlanNodeProps { - node: FlatPlanNode; -} - -class PlanNode extends React.Component { - render() { - if (shouldHideNode(this.props.node.name)) { - return null; - } - const node = this.props.node; - return ( -
  • - - {node.children && - node.children.map((child) => )} -
  • - ); - } -} - -interface PlanViewProps { - title: string; - plan: IExplainTreePlanNode; -} - -interface PlanViewState { - expanded: boolean; - showExpandDirections: boolean; -} - -export class PlanView extends React.Component { - private innerContainer: React.RefObject; - constructor(props: PlanViewProps) { - super(props); - this.state = { - expanded: false, - showExpandDirections: true, - }; - this.innerContainer = React.createRef(); - } - - toggleExpanded = () => { - this.setState((state) => ({ - expanded: !state.expanded, - })); - }; - - showExpandDirections() { - // Only show directions to show/hide the full plan if content is longer than its max-height. - const containerObj = this.innerContainer.current; - return containerObj.scrollHeight > containerObj.clientHeight; - } - - componentDidMount() { - this.setState({ showExpandDirections: this.showExpandDirections() }); - } - - render() { - const flattenedPlanNodes = flattenTree(this.props.plan); - - const lastSampledHelpText = ( - - If the time from the last sample is greater than 5 minutes, a new plan - will be sampled. This frequency can be configured with the cluster - setting{" "} - -
    -            sql.metrics.statement_details.plan_collection.period
    -          
    -
    - . -
    - ); - - return ( -
    - - - - - - - - - - -
    -

    - {this.props.title} -

    -
    - -
    -
    i
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - ); - } -} diff --git a/pkg/ui/src/views/statements/statementDetails.tsx b/pkg/ui/src/views/statements/statementDetails.tsx index db90c64031ab..2359791272c8 100644 --- a/pkg/ui/src/views/statements/statementDetails.tsx +++ b/pkg/ui/src/views/statements/statementDetails.tsx @@ -30,7 +30,6 @@ import { } from "src/util/appStats"; import { appAttr, implicitTxnAttr, statementAttr } from "src/util/constants"; import { FixLong } from "src/util/fixLong"; -import { AggregateStatistics } from "./statementsTable"; import { getMatchParamByName } from "src/util/query"; import { selectDiagnosticsReportsByStatementFingerprint } from "src/redux/statements/statementsSelectors"; import { @@ -38,6 +37,7 @@ import { StatementDetailsDispatchProps, StatementDetailsStateProps, StatementDetailsProps, + AggregateStatistics, } from "@cockroachlabs/admin-ui-components"; import { createStatementDiagnosticsReportAction } from "src/redux/statements"; import { createStatementDiagnosticsAlertLocalSetting } from "src/redux/alerts"; diff --git a/pkg/ui/src/views/statements/statementsTable.module.styl b/pkg/ui/src/views/statements/statementsTable.module.styl deleted file mode 100644 index 4bb7547592f7..000000000000 --- a/pkg/ui/src/views/statements/statementsTable.module.styl +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -@require '~styl/base/palette.styl' -@require '~src/components/core/index' - -.statements-table__col-time - white-space nowrap - -.statements-table__col-count--bar-chart - width 100px - -.statements-table__col-retries--bar-chart - width 80px - -.numeric-stats-table - .bar-chart - width 200px - -.statements-table__col-rows, .statements-table__col-latency - &--bar-chart - min-width 150px - -.statements-table__col-count, .statements-table__col-retries, .statements-table__col-rows - &--bar-chart - margin-left 0 - - &__label - left 0 - width 40px - min-width 40px - -.cl-table__col-query-text a - font-family RobotoMono-Medium - font-size 12px - line-height 1.83 - color $adminui-grey-1 - width 400px - text-decoration none - cursor pointer - &:hover - color $colors--primary-blue-3 - text-decoration underline diff --git a/pkg/ui/src/views/statements/statementsTable.stories.tsx b/pkg/ui/src/views/statements/statementsTable.stories.tsx deleted file mode 100644 index 7ee3a214189f..000000000000 --- a/pkg/ui/src/views/statements/statementsTable.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { storiesOf } from "@storybook/react"; -import { - makeStatementsColumns, - StatementsSortedTable, -} from "./statementsTable"; -import statementsPagePropsFixture from "src/views/statements/statementsPage.fixture"; -import { withRouterProvider } from ".storybook/decorators"; - -const { statements } = statementsPagePropsFixture; - -storiesOf("StatementsSortedTable", module) - .addDecorator(withRouterProvider) - .add("with data", () => ( - - )) - .add("empty table", () => ); diff --git a/pkg/ui/src/views/statements/statementsTable.tsx b/pkg/ui/src/views/statements/statementsTable.tsx deleted file mode 100644 index 2515f97f30fd..000000000000 --- a/pkg/ui/src/views/statements/statementsTable.tsx +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2018 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import classNames from "classnames/bind"; - -import { StatementStatistics } from "src/util/appStats"; -import { FixLong } from "src/util/fixLong"; -import { StatementSummary } from "src/util/sql/summarize"; -import { - ColumnDescriptor, - SortedTable, -} from "src/views/shared/components/sortedtable"; -import { - countBarChart, - latencyBarChart, - retryBarChart, - rowsBarChart, -} from "./barCharts"; -import "./statements.styl"; -import { cockroach } from "src/js/protos"; -import IStatementDiagnosticsReport = cockroach.server.serverpb.IStatementDiagnosticsReport; -import { ActivateDiagnosticsModalRef } from "./diagnostics/activateDiagnosticsModal"; -import styles from "./statementsTable.module.styl"; -import { - StatementTableTitle, - StatementTableCell, - NodeNames, -} from "./statementsTableContent"; -import { getDiagnosticsStatus } from "src/views/statements/diagnostics"; - -const cx = classNames.bind(styles); -const longToInt = (d: number | Long) => FixLong(d).toInt(); - -export interface AggregateStatistics { - // label is either shortStatement (StatementsPage) or nodeId (StatementDetails). - label: string; - implicitTxn: boolean; - stats: StatementStatistics; - drawer?: boolean; - firstCellBordered?: boolean; - diagnosticsReport?: IStatementDiagnosticsReport; -} - -export class StatementsSortedTable extends SortedTable {} - -export function shortStatement(summary: StatementSummary, original: string) { - switch (summary.statement) { - case "update": - return "UPDATE " + summary.table; - case "insert": - return "INSERT INTO " + summary.table; - case "select": - return "SELECT FROM " + summary.table; - case "delete": - return "DELETE FROM " + summary.table; - case "create": - return "CREATE TABLE " + summary.table; - case "set": - return "SET " + summary.table; - default: - return original; - } -} - -export function makeStatementsColumns( - statements: AggregateStatistics[], - selectedApp: string, - search?: string, - activateDiagnosticsRef?: React.RefObject, -): ColumnDescriptor[] { - const columns: ColumnDescriptor[] = [ - { - title: StatementTableTitle.statements, - className: cx("cl-table__col-query-text"), - cell: StatementTableCell.statements(search, selectedApp), - sort: (stmt) => stmt.label, - }, - { - title: StatementTableTitle.txtType, - className: cx("statements-table__col-time"), - cell: (stmt) => (stmt.implicitTxn ? "Implicit" : "Explicit"), - sort: (stmt) => (stmt.implicitTxn ? "Implicit" : "Explicit"), - }, - ]; - columns.push(...makeCommonColumns(statements)); - - if (activateDiagnosticsRef) { - const diagnosticsColumn: ColumnDescriptor = { - title: StatementTableTitle.diagnostics, - cell: StatementTableCell.diagnostics(activateDiagnosticsRef), - sort: (stmt) => { - if (stmt.diagnosticsReport) { - return getDiagnosticsStatus(stmt.diagnosticsReport); - } - return null; - }, - }; - columns.push(diagnosticsColumn); - } - return columns; -} - -export function makeNodesColumns( - statements: AggregateStatistics[], - nodeNames: NodeNames, -): ColumnDescriptor[] { - const original: ColumnDescriptor[] = [ - { - title: null, - cell: StatementTableCell.nodeLink(nodeNames), - // sort: (stmt) => stmt.label, - }, - ]; - - return original.concat(makeCommonColumns(statements)); -} - -function makeCommonColumns( - statements: AggregateStatistics[], -): ColumnDescriptor[] { - const countBar = countBarChart(statements, { - classes: { - root: cx("statements-table__col-count--bar-chart"), - label: cx("statements-table__col-count--bar-chart__label"), - }, - }); - const retryBar = retryBarChart(statements, { - classes: { - root: cx("statements-table__col-retries--bar-chart"), - label: cx("statements-table__col-retries--bar-chart__label"), - }, - }); - const rowsBar = rowsBarChart(statements, { - classes: { - root: cx("statements-table__col-rows--bar-chart"), - label: cx("statements-table__col-rows--bar-chart__label"), - }, - }); - const latencyBar = latencyBarChart(statements, { - classes: { - root: cx("statements-table__col-latency--bar-chart"), - }, - }); - - return [ - { - title: StatementTableTitle.retries, - className: cx("statements-table__col-retries"), - cell: retryBar, - sort: (stmt) => - longToInt(stmt.stats.count) - longToInt(stmt.stats.first_attempt_count), - }, - { - title: StatementTableTitle.executionCount, - className: cx("statements-table__col-count"), - cell: countBar, - sort: (stmt) => FixLong(stmt.stats.count).toInt(), - }, - { - title: StatementTableTitle.rowsAffected, - className: cx("statements-table__col-rows"), - cell: rowsBar, - sort: (stmt) => stmt.stats.num_rows.mean, - }, - { - title: StatementTableTitle.latency, - className: cx("statements-table__col-latency"), - cell: latencyBar, - sort: (stmt) => stmt.stats.service_lat.mean, - }, - ]; -} diff --git a/pkg/ui/src/views/statements/statementsTableContent.tsx b/pkg/ui/src/views/statements/statementsTableContent.tsx deleted file mode 100644 index 1fac494015d6..000000000000 --- a/pkg/ui/src/views/statements/statementsTableContent.tsx +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2020 The Cockroach Authors. -// -// Use of this software is governed by the Business Source License -// included in the file licenses/BSL.txt. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0, included in the file -// licenses/APL.txt. - -import React from "react"; -import { Link } from "react-router-dom"; -import classNames from "classnames/bind"; -import { Anchor, Tooltip } from "src/components"; -import { - statementDiagnostics, - statementsRetries, - statementsSql, - statementsTimeInterval, - transactionalPipelining, -} from "src/util/docs"; -import getHighlightedText from "src/util/highlightedText"; -import { summarize } from "src/util/sql/summarize"; -import { ActivateDiagnosticsModalRef } from "./diagnostics/activateDiagnosticsModal"; -import { DiagnosticStatusBadge } from "./diagnostics/diagnosticStatusBadge"; -import { shortStatement } from "./statementsTable"; -import styles from "./statementsTableContent.module.styl"; -import { getDiagnosticsStatus } from "src/views/statements/diagnostics"; - -export type NodeNames = { [nodeId: string]: string }; - -const cx = classNames.bind(styles); - -export const StatementTableTitle = { - statements: ( - -

    - {"SQL statement "} - - fingerprint. - -

    -

    - To view additional details of a SQL statement fingerprint, click - this to open the Statement Details page. -

    -
    - } - > - Statements - - ), - txtType: ( - -

    - { - "Type of transaction (implicit or explicit). Explicit transactions refer to statements that are wrapped by " - } - BEGIN - {" and "} - COMMIT - {" statements by the client. Explicit transactions employ "} - - transactional pipelining - - { - " and therefore report latencies that do not account for replication." - } -

    -

    - For statements not in explicit transactions, CockroachDB wraps each - statement in individual implicit transactions. -

    -
    - } - > - TXN Type - - ), - diagnostics: ( - -

    - {"Option to activate "} - - diagnostics - - { - " for each statement. If activated, this displays the status of diagnostics collection (" - } - WAITING, READY, OR ERROR). -

    -
    - } - > - Diagnostics - - ), - retries: ( - -

    - {"Cumulative number of "} - - retries - - { - " of statements with this fingerprint within the last hour or specified time interval." - } -

    -
    - } - > - Retries - - ), - executionCount: ( - -

    - { - "Cumulative number of executions of statements with this fingerprint within the last hour or specified " - } - - time interval - - . -

    -

    - {"The bar indicates the ratio of runtime success (gray) to "} - - retries - - {" (red) for the SQL statement fingerprint."} -

    -
    - } - > - Execution Count - - ), - rowsAffected: ( - -

    - { - "Average number of rows returned while executing statements with this fingerprint within the last hour or specified " - } - - time interval - - . -

    -

    - The gray bar indicates the mean number of rows returned. The blue - bar indicates one standard deviation from the mean. -

    -
    - } - > - Rows Affected - - ), - latency: ( - -

    - Average service latency of statements with this fingerprint within - the last hour or specified time interval. -

    -

    - The gray bar indicates the mean latency. The blue bar indicates one - standard deviation from the mean. -

    -
    - } - > - Latency - - ), -}; - -export const StatementTableCell = { - statements: (search?: string, selectedApp?: string) => (stmt: any) => ( - - ), - diagnostics: ( - activateDiagnosticsRef: React.RefObject, - ) => (stmt: any) => { - if (stmt.diagnosticsReport) { - return ( - - ); - } - return ( - - activateDiagnosticsRef?.current?.showModalFor(stmt.label) - } - > - Activate - - ); - }, - nodeLink: (nodeNames: NodeNames) => (stmt: any) => ( - - ), -}; - -interface StatementLinkProps { - statement: string; - app: string; - implicitTxn: boolean; - search: string; - anonStatement?: string; -} - -// StatementLinkTarget returns the link to the relevant statement page, given -// the input statement details. -export const StatementLinkTarget = (props: StatementLinkProps) => { - let base: string; - if (props.app && props.app.length > 0) { - base = `/statements/${props.app}/${props.implicitTxn}`; - } else { - base = `/statement/${props.implicitTxn}`; - } - - let linkStatement = props.statement; - if (props.anonStatement) { - linkStatement = props.anonStatement; - } - return `${base}/${encodeURIComponent(linkStatement)}`; -}; - -export const StatementLink = (props: StatementLinkProps) => { - const summary = summarize(props.statement); - return ( - -
    - - {getHighlightedText(props.statement, props.search)} - - } - overlayClassName={cx("cl-table-link__statement-tooltip--fixed-width")} - > -
    - {getHighlightedText( - shortStatement(summary, props.statement), - props.search, - true, - )} -
    -
    -
    - - ); -}; - -export const NodeLink = (props: { nodeId: string; nodeNames: NodeNames }) => ( - - - {props.nodeNames[props.nodeId]} - - -);