From f28f7d575d92038c854ff4e83742289c75071ce0 Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 5 Jul 2021 15:19:55 +0530 Subject: [PATCH 01/10] filter and selection options for spans Signed-off-by: vvvprabhakar --- .../TracePageHeader/AltViewOptions.tsx | 10 +- .../TracePageHeader/TracePageHeader.track.tsx | 2 + .../TracePage/TraceSpanView/index.css | 47 ++ .../TracePage/TraceSpanView/index.tsx | 428 ++++++++++++++++++ .../TracePage/TraceSpanView/types.tsx | 59 +++ .../TraceStatistics/TraceStatisticsHeader.tsx | 49 +- .../TracePage/TraceStatistics/tableValues.tsx | 4 + .../TracePage/TraceStatistics/types.tsx | 1 + .../src/components/TracePage/index.tsx | 5 +- .../src/components/TracePage/types.tsx | 1 + 10 files changed, 596 insertions(+), 10 deletions(-) create mode 100644 packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css create mode 100644 packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx create mode 100644 packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx index 8d51a0292d..35e435f0d4 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx @@ -21,6 +21,7 @@ import { trackGanttView, trackGraphView, trackStatisticsView, + trackTraceSpansView, trackJsonView, trackRawJsonView, } from './TracePageHeader.track'; @@ -46,6 +47,10 @@ const MENU_ITEMS = [ viewType: ETraceViewType.TraceStatistics, label: 'Trace Statistics', }, + { + viewType: ETraceViewType.TraceSpansView, + label: 'Trace Spans Table', + }, ]; export default function AltViewOptions(props: Props) { @@ -58,6 +63,8 @@ export default function AltViewOptions(props: Props) { trackGraphView(); } else if (item === ETraceViewType.TraceStatistics) { trackStatisticsView(); + } else if (item === ETraceViewType.TraceSpansView) { + trackTraceSpansView(); } onTraceViewChange(item); }; @@ -70,8 +77,7 @@ export default function AltViewOptions(props: Props) { {item.label} - ))} - + ))} trackEvent(CATEGORY_ALT_VIEW, ACTION_GANTT); @@ -32,6 +33,7 @@ export const trackGraphView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_GRAPH); export const trackJsonView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_JSON); export const trackRawJsonView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_RAW_JSON); export const trackStatisticsView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_STATISTICS); +export const trackTraceSpansView = () => trackEvent(CATEGORY_ALT_VIEW, ACTION_TRACE_SPANS_VIEW); export const trackSlimHeaderToggle = (isOpen: boolean) => trackEvent(CATEGORY_SLIM_HEADER, getToggleValue(isOpen)); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css new file mode 100644 index 0000000000..f1edf6da2f --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css @@ -0,0 +1,47 @@ +/* +Copyright (c) 2020 The Jaeger Authors. + +Licensed 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. +*/ + +.title--TraceStatistics { + width: 100%; + padding-top: 0.3%; + padding-bottom: 0.3%; + padding-left: 0.4%; + background: rgb(236, 236, 236); + margin-bottom: 0%; +} + +.DetailTraceTableTbody--TraceStatistics { + border-bottom: 1px solid rgb(204, 204, 204); +} + +.ant-select-dropdown--multiple{ + z-index: 9999; + white-space: nowrap; + overflow: auto +} + + +.span-table .ant-dropdown { + width: 20%; + z-index: 1; +} + +.search-box{ + padding: 8px; + background-color: white; + width: 80%; + border: 1px solid rgba(0, 0, 0, 0.15); +} + diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx new file mode 100644 index 0000000000..088abab3b5 --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -0,0 +1,428 @@ +import React, { Component, useRef } from 'react'; +import { Row, Col, Table, Input, Button, Icon, Select } from 'antd'; +import { TNil } from '../../../types'; +import { Trace, Span } from '../../../types/trace'; +import moment from 'moment'; +import { FilterDropdownProps } from './types' +import './index.css'; +import { ColumnProps } from "antd/es/table"; +import { SorterResult } from 'antd/lib/table' +import { PaginationConfig } from "antd/lib/pagination" +import Highlighter from 'react-highlight-words'; +import { SelectValue } from 'antd/lib/select'; +import { ReactElement } from 'react'; +import FormItem from 'antd/lib/form/FormItem'; +const Option = Select.Option; + + + +function getNestedProperty(path: string, span: any) : string{ + return path.split('.').reduce( (prev, curr) => { + return prev ? prev[curr] : null + }, span || self) +} + +function isSpanValue(attribute:string, span: Span, value:any) { + + return getNestedProperty(attribute, span) + .toString() + .toLowerCase() + .includes(value.toLowerCase()) +} + +function timeConversion(microseconds: number) { + + let milliseconds: number = parseInt((microseconds / 1000).toFixed(2)); + let seconds: number = parseInt((milliseconds / 1000).toFixed(2)); + let minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1)); + let hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1)); + let days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1)); + + if (milliseconds < 1000) { + return milliseconds + "ms" + } else if (seconds < 60) { + return seconds + " Sec"; + } else if (minutes < 60) { + return minutes + " Min"; + } else if (hours < 24) { + return hours + " Hrs"; + } else { + return days + " Days" + } +} + +type Props = { + trace: Trace; + uiFindVertexKeys: Set | TNil; + uiFind: string | null | undefined; +}; + +type State = { + searchText:string; + searchedColumn:string; + data: Span[]; + dataLength:number; + serviceNamesList: string[]; + operationNamesList: string[]; + filtered: { id: keyof Span, value: string[] }[], + selectedServiceName: string[], + selectedOperationName: string[], + filteredData: Span[] + +}; + + +export default class TraceSpanView extends Component { + + onTablePropsChange(pagination: PaginationConfig, filters: any, sorter: SorterResult) { + let filterAttribute= Object.keys(filters); + let data = this.state.data.filter((span)=>{ + return filterAttribute.every((attribute) =>{ + return filters[attribute].every((value: string) => { + return isSpanValue(attribute, span, value) + }) + + }) + + }) + this.setState({ + ...this.state, + data: data, + dataLength:data.length, + + }); + } + + onFiltersChange(attribute:string, value: SelectValue){ + let selected = value as []; + + let datasource= this.state.data.filter(span =>{ + let spanValue = getNestedProperty(attribute, span) as never; + return selected.indexOf(spanValue) > -1 + }); + + + console.log(datasource); + this.setState({ + ...this.state, + data: datasource, + dataLength: datasource.length, + + }); + + } + onServiceNameFiltersChange(value: SelectValue, option: any) { + // this.onFiltersChange('process.serviceName', value) + let selected = value as []; + let datasource = this.state.data.filter(span => { + let spanValue = getNestedProperty('process.serviceName', span) as never; + return selected.indexOf(spanValue) > -1 + }); + + + console.log(datasource); + this.setState({ + ...this.state, + data: datasource, + dataLength: datasource.length, + + }); + + } + onOperationNameFiltersChange(value: SelectValue, option: any) { + this.onFiltersChange('operatioName', value) + + } + + + handleSearch(selectedKeys: string[] , confirm: (() => void) , dataIndex: string): void { + confirm(); + this.setState({ + ...this.state, + searchText: selectedKeys[0], + searchedColumn: dataIndex, + }); + }; + + handleReset(clearFilters: (() => void) ){ + clearFilters(); + this.setState({ + ...this.state, + searchText: '', + data: this.props.trace.spans, + dataLength: this.props.trace.spans.length + }); + }; + + + constructor(props: Props, state: State) { + super(props, state); + const serviceNamesList = new Set(), operationNamesList = new Set(); + + this.props.trace.spans.map((span) => { + serviceNamesList.add(span.process.serviceName); + operationNamesList.add(span.operationName); + }); + + this.state = { + searchText: "", + searchedColumn: "", + data: this.props.trace.spans, + dataLength: this.props.trace.spans.length, + serviceNamesList: [...serviceNamesList], + operationNamesList:[...operationNamesList], + filteredData:this.props.trace.spans, + filtered:[], + selectedServiceName:[], + selectedOperationName:[], + } + this.uniqueOptions = this.uniqueOptions.bind(this); + this.handleFilter = this.handleFilter.bind(this); + + } + + onFilteredChangeCustom(value: string[], accessor: keyof Span) { + + + let filtered = this.state.filtered; + let insertNewFilter = 1; + if (filtered.length) { + console.log("filtered.length " + filtered.length); + filtered.forEach((filter, i) => { + if (filter.id === accessor) { + if (!value) filtered.splice(i, 1); + else filter.value = value; + insertNewFilter = 0; + } + }); + } + + if (insertNewFilter) { + filtered.push({ id: accessor, value: value }); + } + + this.setState({...this.state, filtered: filtered }); + let data =this.state.data.filter(span => this.state.filtered.every(filter => { + let spanValue = getNestedProperty(filter.id, span); + return filter.value.includes(spanValue) + })); + + this.setState({ ...this.state, filteredData: data }); + } + uniqueOperationNameOptions(objectsArray: Span[], objectKey: keyof Span) { + debugger; + var a = objectsArray.map((o) => { + if (this.state.selectedOperationName.length && this.state.selectedOperationName.includes(getNestedProperty( 'process.serviceName', o))){ + return getNestedProperty(objectKey, o); + }else{ + return getNestedProperty(objectKey, o); + } + }); + console.log(a); + return a.filter(function (i, index) { + return a.indexOf(i) >= index; + }); + } + uniqueOptions(objectsArray: Span[], objectKey: keyof Span) { + var a = objectsArray.map((o, i) => { + return getNestedProperty(objectKey, o); + }); + + return a.filter(function (i, index) { + return a.indexOf(i) >= index; + }); +}; + + handleFilter(item:any, itemName:string) { + console.log(item); + this.setState({ + ...this.state, + [itemName]: this.state.selectedServiceName.filter( + a => item.value.indexOf(a) < 0 + ), + filtered: this.state.filtered.filter(a => { + if (item[itemName] === a.value) { + return false; + } + return true; + }) + }); + } + + + getColumnSearchProps = (dataIndex : keyof Span )=> ({ + filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }: FilterDropdownProps) => ( +
+ setSelectedKeys && setSelectedKeys(e.target.value ? [e.target.value] : [])} + onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)} + style={{ width: 220, marginBottom: 8, display: 'block' }} + /> + + +
+ ), + filterIcon: (filtered : boolean) => ( + + ), + onFilter: (value :string, record: Span) =>{ + return isSpanValue(dataIndex, record, value); + }, + + + render: (text: string) => + this.state.searchedColumn === dataIndex ? ( + + ) : ( + text + ), + }); + + + render() { + + const columns: ColumnProps[] = [ + { + title: "Service Name", + dataIndex: "process.serviceName", + width: '25%', + ...this.getColumnSearchProps("process.serviceName" as keyof Span) + + }, { + title: "Operation", + dataIndex: "operationName", + width: '25%', + ...this.getColumnSearchProps("operationName") + + }, { + title: "ID", + dataIndex: "spanID", + render: (cell: string, record: Span) => { + return {record.spanID} ; + } + }, { + title: "Duration", + dataIndex: "duration", + sorter: (a, b) => a.duration - b.duration, + render: (cell: string, record: Span) => { + return timeConversion(parseInt(cell)); + } + }, { + title: "Start Time", + dataIndex: "startTime", + sorter: (a, b) => a.startTime - b.startTime, + render: (cell: number, record: Span) => { + return moment(cell / 1000).format("DD MMM YYYY hh:mm A"); + } + } + ]; + return ( +
+

Trace Tabular View

+ + + + + + + + + + + + + + + + + + + + + + +
+
+ ) + } +} \ No newline at end of file diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx new file mode 100644 index 0000000000..8d32efad46 --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx @@ -0,0 +1,59 @@ +// Copyright (c) 2020 The Jaeger Authors. +// +// Licensed 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 { ColumnFilterItem } from "antd/es/table/interface"; + +export interface ITableSpan { + traceID: string; + type: string; + name: string; + count: number; + total: number; + avg: number; + min: number; + max: number; + selfTotal: number; + selfAvg: number; + selfMin: number; + selfMax: number; + percent: number; + isDetail: boolean; + parentElement: string; + color: string; // If it is a service name, the color will be set. + searchColor: string; + colorToPercent: string; // Color created by percent +} + +export interface ITableValues { + uid: string; + value: number; +} + +export interface IColumnValue { + title: string; + attribute: string; + suffix: string; + isDecimal: boolean; +} + + +export interface FilterDropdownProps { + prefixCls?: string; + setSelectedKeys?: (selectedKeys: string[]) => void; + selectedKeys: string[]; + confirm: () => void; + clearFilters: () => void; + filters?: ColumnFilterItem[]; + getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement; +} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx index 9f78932398..e39343ec50 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx @@ -30,7 +30,8 @@ type Props = { tableValue: ITableSpan[], wholeTable: ITableSpan[], valueNameSelector1: string, - valueNameSelector2: string | null + valueNameSelector2: string | null, + valueNameSelector4: string | null ) => void; }; @@ -38,7 +39,7 @@ type State = { valueNameSelector1: string; valueNameSelector2: string | null; valueNameSelector3: string; - + valueNameSelector4: string | null checkboxStatus: boolean; }; @@ -62,18 +63,21 @@ export default class TraceStatisticsHeader extends Component { getColumnValues('Service Name', this.props.trace), getColumnValues('Service Name', this.props.trace), 'Service Name', + null, null ); this.state = { valueNameSelector1: 'Service Name', valueNameSelector2: null, + valueNameSelector4: null, valueNameSelector3: 'Count', checkboxStatus: false, }; this.setValueNameSelector1 = this.setValueNameSelector1.bind(this); this.setValueNameSelector2 = this.setValueNameSelector2.bind(this); this.setValueNameSelector3 = this.setValueNameSelector3.bind(this); + this.setValueNameSelector4 = this.setValueNameSelector4.bind(this); this.checkboxButton = this.checkboxButton.bind(this); this.clearValue = this.clearValue.bind(this); } @@ -107,7 +111,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, value, null); + this.props.handler(newTableValue, newWohleTable, value, null,null); } /** @@ -137,7 +141,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value); + this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value , null); } /** @@ -158,7 +162,27 @@ export default class TraceStatisticsHeader extends Component { newTableValue, newWohleTable, this.state.valueNameSelector1, - this.state.valueNameSelector2 + this.state.valueNameSelector2, + this.state.valueNameSelector4 + ); + } + + /** + * + */ + setValueNameSelector4(value: string){ + this.setState({ + valueNameSelector4: value, + }); + const newTableValue = generateColor(this.props.tableValue, this.getValue(), this.state.checkboxStatus); + const newWholeTable = generateColor(this.props.wholeTable, this.getValue(), this.state.checkboxStatus); + + this.props.handler( + newTableValue, + newWholeTable, + this.state.valueNameSelector1, + this.state.valueNameSelector2, + this.state.valueNameSelector4 ); } @@ -176,7 +200,8 @@ export default class TraceStatisticsHeader extends Component { newTableValue, newWholeTable, this.state.valueNameSelector1, - this.state.valueNameSelector2 + this.state.valueNameSelector2, + this.state.valueNameSelector4 ); } @@ -198,7 +223,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWholeTable, this.state.valueNameSelector1, null); + this.props.handler(newTableValue, newWholeTable, this.state.valueNameSelector1, null, null); } render() { @@ -209,6 +234,7 @@ export default class TraceStatisticsHeader extends Component { this.state.valueNameSelector1 ); + return (
{ clearValue={this.clearValue} required={false} /> +
{ uiFindVertexKeys={graphFindMatches} /> ); - } else { + } else if (ETraceViewType.TraceStatistics === viewType && headerHeight) { view = ; + } else if(ETraceViewType.TraceSpansView === viewType && headerHeight){ + view = } return ( diff --git a/packages/jaeger-ui/src/components/TracePage/types.tsx b/packages/jaeger-ui/src/components/TracePage/types.tsx index 9dfbf5a514..0abbda1ac0 100644 --- a/packages/jaeger-ui/src/components/TracePage/types.tsx +++ b/packages/jaeger-ui/src/components/TracePage/types.tsx @@ -60,4 +60,5 @@ export enum ETraceViewType { TraceTimelineViewer = 'TraceTimelineViewer', TraceGraph = 'TraceGraph', TraceStatistics = 'TraceStatistics', + TraceSpansView='TraceSpansView' } From e9b4b91527090d4bb9ca3f219a8dd60f3c2b119a Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 5 Jul 2021 17:42:18 +0530 Subject: [PATCH 02/10] eslint issues Signed-off-by: vvvprabhakar --- .../TracePageHeader/AltViewOptions.tsx | 3 +- .../TracePageHeader/TracePageHeader.track.tsx | 2 +- .../TracePage/TraceSpanView/index.css | 8 +- .../TracePage/TraceSpanView/index.tsx | 606 +++++++++--------- .../TracePage/TraceSpanView/types.tsx | 5 +- .../TraceStatistics/TraceStatisticsHeader.tsx | 15 +- .../TracePage/TraceStatistics/tableValues.tsx | 8 +- .../TracePage/TraceStatistics/types.tsx | 2 +- .../src/components/TracePage/index.tsx | 4 +- .../src/components/TracePage/types.tsx | 2 +- .../jaeger-ui/src/utils/tracking/utils.tsx | 1 + 11 files changed, 321 insertions(+), 335 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx index 35e435f0d4..d08d908760 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.tsx @@ -77,7 +77,8 @@ export default function AltViewOptions(props: Props) { {item.label} - ))} + ))} + trackEvent(CATEGORY_ALT_VIEW, ACTION_GANTT); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css index f1edf6da2f..f743d9b7b6 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css @@ -26,22 +26,20 @@ limitations under the License. border-bottom: 1px solid rgb(204, 204, 204); } -.ant-select-dropdown--multiple{ +.ant-select-dropdown--multiple { z-index: 9999; white-space: nowrap; - overflow: auto + overflow: auto; } - .span-table .ant-dropdown { width: 20%; z-index: 1; } -.search-box{ +.search-box { padding: 8px; background-color: white; width: 80%; border: 1px solid rgba(0, 0, 0, 0.15); } - diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index 088abab3b5..cf946e039e 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -1,54 +1,68 @@ -import React, { Component, useRef } from 'react'; +// Copyright (c) 2018 Uber Technologies, Inc. +// +// Licensed 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 React, { Component } from 'react'; import { Row, Col, Table, Input, Button, Icon, Select } from 'antd'; -import { TNil } from '../../../types'; -import { Trace, Span } from '../../../types/trace'; import moment from 'moment'; -import { FilterDropdownProps } from './types' -import './index.css'; -import { ColumnProps } from "antd/es/table"; -import { SorterResult } from 'antd/lib/table' -import { PaginationConfig } from "antd/lib/pagination" -import Highlighter from 'react-highlight-words'; +import { ColumnProps } from 'antd/es/table'; import { SelectValue } from 'antd/lib/select'; -import { ReactElement } from 'react'; import FormItem from 'antd/lib/form/FormItem'; -const Option = Select.Option; - +import './index.css'; +import { TNil } from '../../../types'; +import { Trace, Span } from '../../../types/trace'; +import { IFilterDropdownProps } from './types'; +const Option = Select.Option; -function getNestedProperty(path: string, span: any) : string{ - return path.split('.').reduce( (prev, curr) => { - return prev ? prev[curr] : null - }, span || self) +function getNestedProperty(path: string, span: any): string { + return path.split('.').reduce((prev, curr) => { + return prev ? prev[curr] : null; + }, span); } -function isSpanValue(attribute:string, span: Span, value:any) { - - return getNestedProperty(attribute, span) +function isSpanValue(attribute: string, span: Span, value: any) { + return getNestedProperty(attribute, span) .toString() .toLowerCase() - .includes(value.toLowerCase()) + .includes(value.toLowerCase()); } +function getHighlightedText(text: string, highlight: string) { + const parts = text.split(new RegExp(`(${highlight})`, 'gi')); + return ( + {parts.map(part => (part.toLowerCase() === highlight.toLowerCase() ? {part} : part))} + ); +} function timeConversion(microseconds: number) { - - let milliseconds: number = parseInt((microseconds / 1000).toFixed(2)); - let seconds: number = parseInt((milliseconds / 1000).toFixed(2)); - let minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1)); - let hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1)); - let days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1)); - + const milliseconds: number = parseInt((microseconds / 1000).toFixed(2), 10); + const seconds: number = parseInt((milliseconds / 1000).toFixed(2), 10); + const minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1), 10); + const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1), 10); + const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1), 10); + let timeText; if (milliseconds < 1000) { - return milliseconds + "ms" + timeText = `${milliseconds}ms`; } else if (seconds < 60) { - return seconds + " Sec"; + timeText = `${seconds}Sec`; } else if (minutes < 60) { - return minutes + " Min"; + timeText = `${minutes}Min`; } else if (hours < 24) { - return hours + " Hrs"; + timeText = `${hours}Hrs`; } else { - return days + " Days" + timeText = `${days}Days`; } + return timeText; } type Props = { @@ -58,201 +72,68 @@ type Props = { }; type State = { - searchText:string; - searchedColumn:string; + searchText: string; + searchedColumn: string; data: Span[]; - dataLength:number; + dataLength: number; serviceNamesList: string[]; operationNamesList: string[]; - filtered: { id: keyof Span, value: string[] }[], - selectedServiceName: string[], - selectedOperationName: string[], - filteredData: Span[] - + filtered: { id: keyof Span; value: string[] }[]; + selectedServiceName: string[]; + selectedOperationName: string[]; + filteredData: Span[]; }; - export default class TraceSpanView extends Component { - - onTablePropsChange(pagination: PaginationConfig, filters: any, sorter: SorterResult) { - let filterAttribute= Object.keys(filters); - let data = this.state.data.filter((span)=>{ - return filterAttribute.every((attribute) =>{ - return filters[attribute].every((value: string) => { - return isSpanValue(attribute, span, value) - }) - - }) - - }) - this.setState({ - ...this.state, - data: data, - dataLength:data.length, - - }); - } - - onFiltersChange(attribute:string, value: SelectValue){ - let selected = value as []; - - let datasource= this.state.data.filter(span =>{ - let spanValue = getNestedProperty(attribute, span) as never; - return selected.indexOf(spanValue) > -1 - }); - - - console.log(datasource); - this.setState({ - ...this.state, - data: datasource, - dataLength: datasource.length, - - }); - - } - onServiceNameFiltersChange(value: SelectValue, option: any) { - // this.onFiltersChange('process.serviceName', value) - let selected = value as []; - let datasource = this.state.data.filter(span => { - let spanValue = getNestedProperty('process.serviceName', span) as never; - return selected.indexOf(spanValue) > -1 - }); - - - console.log(datasource); - this.setState({ - ...this.state, - data: datasource, - dataLength: datasource.length, - - }); - - } - onOperationNameFiltersChange(value: SelectValue, option: any) { - this.onFiltersChange('operatioName', value) - - } - - - handleSearch(selectedKeys: string[] , confirm: (() => void) , dataIndex: string): void { - confirm(); - this.setState({ - ...this.state, - searchText: selectedKeys[0], - searchedColumn: dataIndex, - }); - }; - - handleReset(clearFilters: (() => void) ){ - clearFilters(); - this.setState({ - ...this.state, - searchText: '', - data: this.props.trace.spans, - dataLength: this.props.trace.spans.length - }); - }; - - constructor(props: Props, state: State) { super(props, state); - const serviceNamesList = new Set(), operationNamesList = new Set(); + const serviceNamesList = new Set(); + const operationNamesList = new Set(); - this.props.trace.spans.map((span) => { + this.props.trace.spans.map(span => { serviceNamesList.add(span.process.serviceName); operationNamesList.add(span.operationName); + return { serviceNamesList, operationNamesList }; }); this.state = { - searchText: "", - searchedColumn: "", + searchText: '', + searchedColumn: '', data: this.props.trace.spans, dataLength: this.props.trace.spans.length, serviceNamesList: [...serviceNamesList], - operationNamesList:[...operationNamesList], - filteredData:this.props.trace.spans, - filtered:[], - selectedServiceName:[], - selectedOperationName:[], - } - this.uniqueOptions = this.uniqueOptions.bind(this); + operationNamesList: [...operationNamesList], + filteredData: this.props.trace.spans, + filtered: [], + selectedServiceName: [], + selectedOperationName: [], + }; this.handleFilter = this.handleFilter.bind(this); - + this.onTablePropsChange = this.onTablePropsChange.bind(this); } - onFilteredChangeCustom(value: string[], accessor: keyof Span) { - - - let filtered = this.state.filtered; - let insertNewFilter = 1; - if (filtered.length) { - console.log("filtered.length " + filtered.length); - filtered.forEach((filter, i) => { - if (filter.id === accessor) { - if (!value) filtered.splice(i, 1); - else filter.value = value; - insertNewFilter = 0; - } - }); - } - - if (insertNewFilter) { - filtered.push({ id: accessor, value: value }); - } - - this.setState({...this.state, filtered: filtered }); - let data =this.state.data.filter(span => this.state.filtered.every(filter => { - let spanValue = getNestedProperty(filter.id, span); - return filter.value.includes(spanValue) + handleSearch(selectedKeys: string[], confirm: () => void, dataIndex: string): void { + confirm(); + this.setState(previousState => ({ + ...previousState, + searchText: selectedKeys[0], + searchedColumn: dataIndex, })); - - this.setState({ ...this.state, filteredData: data }); } - uniqueOperationNameOptions(objectsArray: Span[], objectKey: keyof Span) { - debugger; - var a = objectsArray.map((o) => { - if (this.state.selectedOperationName.length && this.state.selectedOperationName.includes(getNestedProperty( 'process.serviceName', o))){ - return getNestedProperty(objectKey, o); - }else{ - return getNestedProperty(objectKey, o); - } - }); - console.log(a); - return a.filter(function (i, index) { - return a.indexOf(i) >= index; - }); - } - uniqueOptions(objectsArray: Span[], objectKey: keyof Span) { - var a = objectsArray.map((o, i) => { - return getNestedProperty(objectKey, o); - }); - - return a.filter(function (i, index) { - return a.indexOf(i) >= index; - }); -}; - handleFilter(item:any, itemName:string) { - console.log(item); - this.setState({ - ...this.state, - [itemName]: this.state.selectedServiceName.filter( - a => item.value.indexOf(a) < 0 - ), - filtered: this.state.filtered.filter(a => { - if (item[itemName] === a.value) { - return false; - } - return true; - }) - }); + handleReset(clearFilters: () => void) { + clearFilters(); + this.setState(previousState => ({ + ...previousState, + searchText: '', + data: this.props.trace.spans, + dataLength: this.props.trace.spans.length, + })); } - - getColumnSearchProps = (dataIndex : keyof Span )=> ({ - filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }: FilterDropdownProps) => ( -
+ getColumnSearchProps = (dataIndex: keyof Span) => ({ + filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }: IFilterDropdownProps) => ( +
{
), - filterIcon: (filtered : boolean) => ( + filterIcon: (filtered: boolean) => ( ), - onFilter: (value :string, record: Span) =>{ + onFilter: (value: string, record: Span) => { return isSpanValue(dataIndex, record, value); }, - render: (text: string) => - this.state.searchedColumn === dataIndex ? ( - - ) : ( - text - ), + this.state.searchedColumn === dataIndex + ? getHighlightedText(text.toString(), this.state.searchText) + : text, }); - + handleFilter(item: any, itemName: string) { + this.setState(previousState => ({ + ...previousState, + [itemName]: previousState.selectedServiceName.filter(a => item.value.indexOf(a) < 0), + filtered: previousState.filtered.filter(a => { + if (item[itemName] === a.value) { + return false; + } + return true; + }), + })); + } + + uniqueOperationNameOptions(objectsArray: Span[], objectKey: keyof Span) { + let operationsList; + const a = objectsArray.map(o => { + if ( + this.state.selectedOperationName.length && + this.state.selectedOperationName.includes(getNestedProperty('process.serviceName', o)) + ) { + operationsList = getNestedProperty(objectKey, o); + } else { + operationsList = getNestedProperty(objectKey, o); + } + return operationsList; + }); + + return a.filter((i, index) => { + return a.indexOf(i) >= index; + }); + } + + uniqueOptions(objectKey: keyof Span) { + const a = this.state.data.map(o => { + return getNestedProperty(objectKey, o); + }); + return a.filter((i, index) => { + return a.indexOf(i) >= index; + }); + } + + onFilteredChangeCustom(value: string[], accessor: keyof Span) { + const filtered = this.state.filtered; + let insertNewFilter = 1; + if (filtered.length) { + filtered.forEach((filter, i) => { + if (filter.id === accessor) { + if (!value) filtered.splice(i, 1); + // else filter.value = value; + insertNewFilter = 0; + } + }); + } + + if (insertNewFilter) { + filtered.push({ id: accessor, value }); + } + + this.setState(previousState => ({ + ...previousState, + filtered, + })); + const data = this.state.data.filter(span => + this.state.filtered.every(filter => { + const spanValue = getNestedProperty(filter.id, span); + return filter.value.includes(spanValue); + }) + ); + + this.setState(previousState => ({ + ...previousState, + filteredData: data, + })); + } + + onTablePropsChange(filters: any) { + const filterAttribute = Object.keys(filters); + const data = this.state.data.filter(span => { + return filterAttribute.every(attribute => { + return filters[attribute].every((value: string) => { + return isSpanValue(attribute, span, value); + }); + }); + }); + this.setState(previousState => ({ + ...previousState, + data, + dataLength: data.length, + })); + } + + onFiltersChange(attribute: string, value: SelectValue) { + const selected = value as []; + const datasource = this.state.data.filter(span => { + const spanValue = getNestedProperty(attribute, span) as never; + return selected.indexOf(spanValue) > -1; + }); + this.setState(previousState => ({ + ...previousState, + data: datasource, + dataLength: datasource.length, + })); + } + + onServiceNameFiltersChange(value: SelectValue) { + // this.onFiltersChange('process.serviceName', value) + const selected = value as []; + const datasource = this.state.data.filter(span => { + const spanValue = getNestedProperty('process.serviceName', span) as never; + return selected.indexOf(spanValue) > -1; + }); + + this.setState(previousState => ({ + ...previousState, + data: datasource, + dataLength: datasource.length, + })); + } + + onOperationNameFiltersChange(value: SelectValue) { + this.onFiltersChange('operatioName', value); + } + render() { - const columns: ColumnProps[] = [ { - title: "Service Name", - dataIndex: "process.serviceName", + title: 'Service Name', + dataIndex: 'process.serviceName', width: '25%', - ...this.getColumnSearchProps("process.serviceName" as keyof Span) - - }, { - title: "Operation", - dataIndex: "operationName", + ...this.getColumnSearchProps('process.serviceName' as keyof Span), + }, + { + title: 'Operation', + dataIndex: 'operationName', width: '25%', - ...this.getColumnSearchProps("operationName") - - }, { - title: "ID", - dataIndex: "spanID", - render: (cell: string, record: Span) => { - return {record.spanID} ; - } - }, { - title: "Duration", - dataIndex: "duration", + ...this.getColumnSearchProps('operationName'), + }, + { + title: 'ID', + dataIndex: 'spanID', + render: (record: Span) => { + return ( + + {' '} + {record.spanID}{' '} + + ); + }, + }, + { + title: 'Duration', + dataIndex: 'duration', sorter: (a, b) => a.duration - b.duration, - render: (cell: string, record: Span) => { - return timeConversion(parseInt(cell)); - } - }, { - title: "Start Time", - dataIndex: "startTime", + render: (cell: string) => { + return timeConversion(parseInt(cell, 10)); + }, + }, + { + title: 'Start Time', + dataIndex: 'startTime', sorter: (a, b) => a.startTime - b.startTime, - render: (cell: number, record: Span) => { - return moment(cell / 1000).format("DD MMM YYYY hh:mm A"); - } - } + render: (cell: number) => { + return moment(cell / 1000).format('DD MMM YYYY hh:mm A'); + }, + }, ]; return (

Trace Tabular View

- + - - - { + this.setState(previousState => ({ + ...previousState, + selectedOperationName: entry as [], + })); + this.onFilteredChangeCustom(entry as [], 'operationName'); }} - > - {this.uniqueOperationNameOptions(this.state.data, "operationName").map( - (name) => { - return ; - } - )} - - - + > + {this.uniqueOperationNameOptions(this.state.data, 'operationName').map(name => { + return ; + })} + + + + + + + - - - - - - - -
+ rowKey="spanID" + />
- ) + ); } -} \ No newline at end of file +} diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx index 8d32efad46..361e07d345 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/types.tsx @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ColumnFilterItem } from "antd/es/table/interface"; +import { ColumnFilterItem } from 'antd/es/table/interface'; export interface ITableSpan { traceID: string; @@ -47,8 +47,7 @@ export interface IColumnValue { isDecimal: boolean; } - -export interface FilterDropdownProps { +export interface IFilterDropdownProps { prefixCls?: string; setSelectedKeys?: (selectedKeys: string[]) => void; selectedKeys: string[]; diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx index e39343ec50..65269e770b 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx @@ -39,7 +39,7 @@ type State = { valueNameSelector1: string; valueNameSelector2: string | null; valueNameSelector3: string; - valueNameSelector4: string | null + valueNameSelector4: string | null; checkboxStatus: boolean; }; @@ -111,7 +111,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, value, null,null); + this.props.handler(newTableValue, newWohleTable, value, null, null); } /** @@ -141,7 +141,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value , null); + this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value, null); } /** @@ -168,15 +168,15 @@ export default class TraceStatisticsHeader extends Component { } /** - * + * */ - setValueNameSelector4(value: string){ + setValueNameSelector4(value: string) { this.setState({ valueNameSelector4: value, }); const newTableValue = generateColor(this.props.tableValue, this.getValue(), this.state.checkboxStatus); const newWholeTable = generateColor(this.props.wholeTable, this.getValue(), this.state.checkboxStatus); - + this.props.handler( newTableValue, newWholeTable, @@ -234,7 +234,6 @@ export default class TraceStatisticsHeader extends Component { this.state.valueNameSelector1 ); - return (
{ { ); } else if (ETraceViewType.TraceStatistics === viewType && headerHeight) { view = ; - } else if(ETraceViewType.TraceSpansView === viewType && headerHeight){ - view = + } else if (ETraceViewType.TraceSpansView === viewType && headerHeight) { + view = ; } return ( diff --git a/packages/jaeger-ui/src/components/TracePage/types.tsx b/packages/jaeger-ui/src/components/TracePage/types.tsx index 0abbda1ac0..8a11f6bed1 100644 --- a/packages/jaeger-ui/src/components/TracePage/types.tsx +++ b/packages/jaeger-ui/src/components/TracePage/types.tsx @@ -60,5 +60,5 @@ export enum ETraceViewType { TraceTimelineViewer = 'TraceTimelineViewer', TraceGraph = 'TraceGraph', TraceStatistics = 'TraceStatistics', - TraceSpansView='TraceSpansView' + TraceSpansView = 'TraceSpansView', } diff --git a/packages/jaeger-ui/src/utils/tracking/utils.tsx b/packages/jaeger-ui/src/utils/tracking/utils.tsx index b7fb53873b..75f826da59 100644 --- a/packages/jaeger-ui/src/utils/tracking/utils.tsx +++ b/packages/jaeger-ui/src/utils/tracking/utils.tsx @@ -14,6 +14,7 @@ import ReactGA from 'react-ga'; +// eslint-disable-next-line import/prefer-default-export export const logTrackingCalls = () => { const calls = ReactGA.testModeAPI.calls; for (let i = 0; i < calls.length; i++) { From caa30a048b523328290eff78e9332c04461d71b9 Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Wed, 14 Jul 2021 17:18:59 +0530 Subject: [PATCH 03/10] eslint issues Signed-off-by: vvvprabhakar --- .../TracePageHeader/AltViewOptions.test.js | 13 ++++++++++++- .../__snapshots__/AltViewOptions.test.js.snap | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js index c2a0d93561..6e65b7e43b 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js @@ -26,6 +26,7 @@ describe('AltViewOptions', () => { let trackJsonView; let trackRawJsonView; let trackStatisticsView; + let trackTraceSpansView; let wrapper; const getLink = text => { @@ -55,6 +56,7 @@ describe('AltViewOptions', () => { trackJsonView = jest.spyOn(track, 'trackJsonView'); trackRawJsonView = jest.spyOn(track, 'trackRawJsonView'); trackStatisticsView = jest.spyOn(track, 'trackStatisticsView'); + trackTraceSpansView = jest.spyOn(track, 'trackTraceSpansView'); }); beforeEach(() => { @@ -78,6 +80,7 @@ describe('AltViewOptions', () => { expect(trackJsonView).toHaveBeenCalledTimes(1); expect(trackGanttView).not.toHaveBeenCalled(); expect(trackGraphView).not.toHaveBeenCalled(); + expect(trackTraceSpansView).not.toHaveBeenCalled(); }); it('track dropdown menu', () => { @@ -99,11 +102,19 @@ describe('AltViewOptions', () => { onTraceViewChangeArg: ETraceViewType.TraceTimelineViewer, propViewType: ETraceViewType.TraceStatisticsView, }, + { + link: 'Trace Spans Table', + trackFn: trackTraceSpansView, + onTraceViewChangeArg: ETraceViewType.TraceSpansView, + propViewType: ETraceViewType.TraceTimelineViewer, + }, ]; viewInteractions.forEach(({ link, trackFn, propViewType }, i) => { if (propViewType) { - wrapper.setProps({ viewType: propViewType }); + wrapper.setProps({ + viewType: propViewType, + }); } expect(props.onTraceViewChange).toHaveBeenCalledTimes(i); expect(trackFn).not.toHaveBeenCalled(); diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap index 9ee4763e14..1147e66dab 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/__snapshots__/AltViewOptions.test.js.snap @@ -27,6 +27,14 @@ exports[`AltViewOptions renders correctly 1`] = ` Trace Statistics + + + Trace Spans Table + + Date: Thu, 15 Jul 2021 16:16:48 +0530 Subject: [PATCH 04/10] fixing test case Signed-off-by: vvvprabhakar --- .../components/TracePage/TracePageHeader/AltViewOptions.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js index 6e65b7e43b..1c28c14b9a 100644 --- a/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TracePageHeader/AltViewOptions.test.js @@ -37,7 +37,7 @@ describe('AltViewOptions', () => { if (link.children().text() === text) return link; } const links2 = menu.find('a'); - for (let i = 0; i < links.length; i++) { + for (let i = 0; i < links2.length; i++) { const link = links2.at(i); if (link.children().text() === text) return link; } From 66435d4e1332a16d9f40e9404ceb02db2490f99c Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Sat, 17 Jul 2021 12:00:53 +0530 Subject: [PATCH 05/10] optimised the filtering logic Signed-off-by: vvvprabhakar --- .../TracePage/TraceSpanView/index.tsx | 205 +++++------------- 1 file changed, 53 insertions(+), 152 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index cf946e039e..94c8780728 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -13,7 +13,7 @@ // limitations under the License. import React, { Component } from 'react'; -import { Row, Col, Table, Input, Button, Icon, Select } from 'antd'; +import { Row, Col, Table, Button, Select } from 'antd'; import moment from 'moment'; import { ColumnProps } from 'antd/es/table'; import { SelectValue } from 'antd/lib/select'; @@ -21,7 +21,6 @@ import FormItem from 'antd/lib/form/FormItem'; import './index.css'; import { TNil } from '../../../types'; import { Trace, Span } from '../../../types/trace'; -import { IFilterDropdownProps } from './types'; const Option = Select.Option; @@ -31,19 +30,6 @@ function getNestedProperty(path: string, span: any): string { }, span); } -function isSpanValue(attribute: string, span: Span, value: any) { - return getNestedProperty(attribute, span) - .toString() - .toLowerCase() - .includes(value.toLowerCase()); -} - -function getHighlightedText(text: string, highlight: string) { - const parts = text.split(new RegExp(`(${highlight})`, 'gi')); - return ( - {parts.map(part => (part.toLowerCase() === highlight.toLowerCase() ? {part} : part))} - ); -} function timeConversion(microseconds: number) { const milliseconds: number = parseInt((microseconds / 1000).toFixed(2), 10); const seconds: number = parseInt((milliseconds / 1000).toFixed(2), 10); @@ -51,7 +37,9 @@ function timeConversion(microseconds: number) { const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1), 10); const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1), 10); let timeText; - if (milliseconds < 1000) { + if (microseconds < 1000) { + timeText = `${microseconds}μs`; + } else if (milliseconds < 1000) { timeText = `${milliseconds}ms`; } else if (seconds < 60) { timeText = `${seconds}Sec`; @@ -75,10 +63,10 @@ type State = { searchText: string; searchedColumn: string; data: Span[]; - dataLength: number; serviceNamesList: string[]; operationNamesList: string[]; - filtered: { id: keyof Span; value: string[] }[]; + serviceNameOperationsMap: Map; + filtered: Record; selectedServiceName: string[]; selectedOperationName: string[]; filteredData: Span[]; @@ -89,27 +77,30 @@ export default class TraceSpanView extends Component { super(props, state); const serviceNamesList = new Set(); const operationNamesList = new Set(); + const serviceNameOperationsMap = new Map(); this.props.trace.spans.map(span => { serviceNamesList.add(span.process.serviceName); operationNamesList.add(span.operationName); + const operationNames = serviceNameOperationsMap.get(span.process.serviceName) || []; + operationNames.push(span.operationName); + serviceNameOperationsMap.set(span.process.serviceName, operationNames); return { serviceNamesList, operationNamesList }; }); - this.state = { searchText: '', searchedColumn: '', data: this.props.trace.spans, - dataLength: this.props.trace.spans.length, serviceNamesList: [...serviceNamesList], operationNamesList: [...operationNamesList], + serviceNameOperationsMap, filteredData: this.props.trace.spans, - filtered: [], + filtered: {}, selectedServiceName: [], selectedOperationName: [], }; - this.handleFilter = this.handleFilter.bind(this); - this.onTablePropsChange = this.onTablePropsChange.bind(this); + this.handleResetFilter = this.handleResetFilter.bind(this); + this.uniqueOperationNameOptions = this.uniqueOperationNameOptions.bind(this); } handleSearch(selectedKeys: string[], confirm: () => void, dataIndex: string): void { @@ -127,138 +118,55 @@ export default class TraceSpanView extends Component { ...previousState, searchText: '', data: this.props.trace.spans, - dataLength: this.props.trace.spans.length, })); } - getColumnSearchProps = (dataIndex: keyof Span) => ({ - filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }: IFilterDropdownProps) => ( -
- setSelectedKeys && setSelectedKeys(e.target.value ? [e.target.value] : [])} - onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)} - style={{ width: 220, marginBottom: 8, display: 'block' }} - /> - - -
- ), - filterIcon: (filtered: boolean) => ( - - ), - onFilter: (value: string, record: Span) => { - return isSpanValue(dataIndex, record, value); - }, - - render: (text: string) => - this.state.searchedColumn === dataIndex - ? getHighlightedText(text.toString(), this.state.searchText) - : text, - }); - - handleFilter(item: any, itemName: string) { + handleResetFilter() { this.setState(previousState => ({ - ...previousState, - [itemName]: previousState.selectedServiceName.filter(a => item.value.indexOf(a) < 0), - filtered: previousState.filtered.filter(a => { - if (item[itemName] === a.value) { - return false; - } - return true; - }), + selectedServiceName: [], + selectedOperationName: [], + filteredData: previousState.data, })); } - uniqueOperationNameOptions(objectsArray: Span[], objectKey: keyof Span) { - let operationsList; - const a = objectsArray.map(o => { - if ( - this.state.selectedOperationName.length && - this.state.selectedOperationName.includes(getNestedProperty('process.serviceName', o)) - ) { - operationsList = getNestedProperty(objectKey, o); - } else { - operationsList = getNestedProperty(objectKey, o); - } - return operationsList; - }); - - return a.filter((i, index) => { - return a.indexOf(i) >= index; - }); - } - - uniqueOptions(objectKey: keyof Span) { - const a = this.state.data.map(o => { - return getNestedProperty(objectKey, o); - }); - return a.filter((i, index) => { - return a.indexOf(i) >= index; - }); + uniqueOperationNameOptions() { + let operationNamesList: string[] = []; + const serviceNameOperationsMap = this.state.serviceNameOperationsMap; + if (this.state.filtered['process.serviceName']) { + this.state.filtered['process.serviceName'].forEach((currentValue: any) => { + operationNamesList = operationNamesList.concat(serviceNameOperationsMap.get(currentValue) || []); + }); + } else { + operationNamesList = this.state.operationNamesList; + } + return [...new Set(operationNamesList)]; } - onFilteredChangeCustom(value: string[], accessor: keyof Span) { + onFilteredChangeCustom(selectedValues: string[], accessor: keyof Span) { const filtered = this.state.filtered; - let insertNewFilter = 1; - if (filtered.length) { - filtered.forEach((filter, i) => { - if (filter.id === accessor) { - if (!value) filtered.splice(i, 1); - // else filter.value = value; - insertNewFilter = 0; + filtered[accessor] = selectedValues; + const data = this.state.data.filter(span => { + let isSpanIncluded; + Object.keys(filtered).every(filterColumn => { + if (filtered[filterColumn].length) { + const spanValue = getNestedProperty(filterColumn, span); + isSpanIncluded = filtered[filterColumn].includes(spanValue); + } else { + isSpanIncluded = true; } + return isSpanIncluded; }); - } - if (insertNewFilter) { - filtered.push({ id: accessor, value }); - } + return isSpanIncluded; + }); this.setState(previousState => ({ ...previousState, filtered, - })); - const data = this.state.data.filter(span => - this.state.filtered.every(filter => { - const spanValue = getNestedProperty(filter.id, span); - return filter.value.includes(spanValue); - }) - ); - - this.setState(previousState => ({ - ...previousState, filteredData: data, })); } - onTablePropsChange(filters: any) { - const filterAttribute = Object.keys(filters); - const data = this.state.data.filter(span => { - return filterAttribute.every(attribute => { - return filters[attribute].every((value: string) => { - return isSpanValue(attribute, span, value); - }); - }); - }); - this.setState(previousState => ({ - ...previousState, - data, - dataLength: data.length, - })); - } - onFiltersChange(attribute: string, value: SelectValue) { const selected = value as []; const datasource = this.state.data.filter(span => { @@ -268,12 +176,10 @@ export default class TraceSpanView extends Component { this.setState(previousState => ({ ...previousState, data: datasource, - dataLength: datasource.length, })); } onServiceNameFiltersChange(value: SelectValue) { - // this.onFiltersChange('process.serviceName', value) const selected = value as []; const datasource = this.state.data.filter(span => { const spanValue = getNestedProperty('process.serviceName', span) as never; @@ -283,7 +189,6 @@ export default class TraceSpanView extends Component { this.setState(previousState => ({ ...previousState, data: datasource, - dataLength: datasource.length, })); } @@ -297,26 +202,19 @@ export default class TraceSpanView extends Component { title: 'Service Name', dataIndex: 'process.serviceName', width: '25%', - ...this.getColumnSearchProps('process.serviceName' as keyof Span), }, { title: 'Operation', dataIndex: 'operationName', width: '25%', - ...this.getColumnSearchProps('operationName'), }, { title: 'ID', dataIndex: 'spanID', - render: (record: Span) => { + render: (text: any, record: Span) => { return ( - - {' '} - {record.spanID}{' '} + + {text} ); }, @@ -341,7 +239,7 @@ export default class TraceSpanView extends Component { return (

Trace Tabular View

- + @@ -374,6 +273,7 @@ export default class TraceSpanView extends Component { mode="multiple" style={{ width: '100%' }} maxTagCount={4} + value={this.state.selectedOperationName} maxTagPlaceholder={`+ ${this.state.selectedOperationName.length - 4} Selected`} placeholder="Please Select Operation" onChange={entry => { @@ -384,7 +284,7 @@ export default class TraceSpanView extends Component { this.onFilteredChangeCustom(entry as [], 'operationName'); }} > - {this.uniqueOperationNameOptions(this.state.data, 'operationName').map(name => { + {this.uniqueOperationNameOptions().map((name: string) => { return ; })} @@ -392,7 +292,9 @@ export default class TraceSpanView extends Component { - + @@ -401,7 +303,6 @@ export default class TraceSpanView extends Component { className="span-table" columns={columns} dataSource={this.state.filteredData} - onChange={this.onTablePropsChange} pagination={{ total: this.state.filteredData.length, pageSizeOptions: ['10', '20', '50', '100'], From 65a9f81df591476283334ad54122ba14e07a55f1 Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 9 Aug 2021 13:55:56 +0530 Subject: [PATCH 06/10] PR comments and Tests Signed-off-by: vvvprabhakar --- .../TracePage/TraceSpanView/index.css | 2 +- .../TracePage/TraceSpanView/index.test.js | 73 ++++++++++++++++++ .../TracePage/TraceSpanView/index.tsx | 75 +++++-------------- .../src/components/TracePage/index.test.js | 9 +++ packages/jaeger-ui/src/utils/date.tsx | 23 ++++++ 5 files changed, 123 insertions(+), 59 deletions(-) create mode 100644 packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css index f743d9b7b6..efb1d6230c 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.css @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.title--TraceStatistics { +.title--TraceSpanView { width: 100%; padding-top: 0.3%; padding-bottom: 0.3%; diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js new file mode 100644 index 0000000000..162072c885 --- /dev/null +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js @@ -0,0 +1,73 @@ +// Copyright (c) 2018 The Jaeger Authors. +// +// Licensed 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 React from 'react'; +import { mount, shallow } from 'enzyme'; +import TraceSpanView from './index'; +import transformTraceData from '../../../model/transform-trace-data'; + +const testTrace = require('../TraceStatistics/tableValuesTestTrace/testTrace.json'); + +const transformedTrace = transformTraceData(testTrace); + +describe('', () => { + let wrapper; + let defaultProps; + + beforeEach(() => { + defaultProps = { + trace: transformedTrace, + uiFind: undefined, + uiFindVertexKeys: undefined, + }; + + wrapper = mount(); + }); + + it('does not explode', () => { + expect(wrapper).toBeDefined(); + expect(wrapper.find('.title--TraceSpanView').length).toBe(1); + expect(wrapper.find('.span-view-table').length).toBe(2); + expect(wrapper.find('table').length).toBe(1); + expect(wrapper.find('colgroup').length).toBe(1); + expect(wrapper.find('Pagination').length).toBe(2); + + expect(wrapper.find('Button').length).toBe(1); + expect(wrapper.find('.ant-form-item-control').length).toBe(3); + }); + it('Should change value when onChange was called', () => { + const event = ['service2']; + wrapper = shallow(); + // wrapper.state('filtered.process.serviceName', ['service1', 'service2']); + wrapper.find('.serviceNameDD Select').simulate('change', event); + expect(wrapper.state('selectedServiceName')).toEqual(['service2']); + }); + it('Should change value when onChange OperatioName DDwas called', () => { + const event = ['op2', 'op3']; + wrapper = shallow(); + wrapper.find('.operationNameDD Select').simulate('change', event); + expect(wrapper.state('selectedOperationName')).toEqual( ['op2', 'op3']); + }); + it('check handler', () => { + const instance = wrapper.instance(); + + expect(instance.state.serviceNamesList).toBeDefined(); + expect(instance.state.serviceNamesList.length).toBe(2); + expect(instance.state.serviceNamesList).toEqual(['service1', 'service2']); + expect(instance.state.operationNamesList).toBeDefined(); + expect(instance.state.operationNamesList.length).toBe(6); + expect(instance.state.operationNamesList).toEqual(['op1', 'op2', 'op3', 'op4', 'op6', 'op7']); + + }); +}); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index 94c8780728..72a1b3f892 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -21,6 +21,7 @@ import FormItem from 'antd/lib/form/FormItem'; import './index.css'; import { TNil } from '../../../types'; import { Trace, Span } from '../../../types/trace'; +import { timeConversion } from '../../../utils/date'; const Option = Select.Option; @@ -30,29 +31,6 @@ function getNestedProperty(path: string, span: any): string { }, span); } -function timeConversion(microseconds: number) { - const milliseconds: number = parseInt((microseconds / 1000).toFixed(2), 10); - const seconds: number = parseInt((milliseconds / 1000).toFixed(2), 10); - const minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1), 10); - const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1), 10); - const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1), 10); - let timeText; - if (microseconds < 1000) { - timeText = `${microseconds}μs`; - } else if (milliseconds < 1000) { - timeText = `${milliseconds}ms`; - } else if (seconds < 60) { - timeText = `${seconds}Sec`; - } else if (minutes < 60) { - timeText = `${minutes}Min`; - } else if (hours < 24) { - timeText = `${hours}Hrs`; - } else { - timeText = `${days}Days`; - } - return timeText; -} - type Props = { trace: Trace; uiFindVertexKeys: Set | TNil; @@ -79,14 +57,14 @@ export default class TraceSpanView extends Component { const operationNamesList = new Set(); const serviceNameOperationsMap = new Map(); - this.props.trace.spans.map(span => { + this.props.trace.spans.forEach(span => { serviceNamesList.add(span.process.serviceName); operationNamesList.add(span.operationName); const operationNames = serviceNameOperationsMap.get(span.process.serviceName) || []; operationNames.push(span.operationName); serviceNameOperationsMap.set(span.process.serviceName, operationNames); - return { serviceNamesList, operationNamesList }; }); + this.state = { searchText: '', searchedColumn: '', @@ -167,35 +145,6 @@ export default class TraceSpanView extends Component { })); } - onFiltersChange(attribute: string, value: SelectValue) { - const selected = value as []; - const datasource = this.state.data.filter(span => { - const spanValue = getNestedProperty(attribute, span) as never; - return selected.indexOf(spanValue) > -1; - }); - this.setState(previousState => ({ - ...previousState, - data: datasource, - })); - } - - onServiceNameFiltersChange(value: SelectValue) { - const selected = value as []; - const datasource = this.state.data.filter(span => { - const spanValue = getNestedProperty('process.serviceName', span) as never; - return selected.indexOf(spanValue) > -1; - }); - - this.setState(previousState => ({ - ...previousState, - data: datasource, - })); - } - - onOperationNameFiltersChange(value: SelectValue) { - this.onFiltersChange('operatioName', value); - } - render() { const columns: ColumnProps[] = [ { @@ -238,10 +187,15 @@ export default class TraceSpanView extends Component { ]; return (
-

Trace Tabular View

+

Trace Tabular View

- + { ', () => { expect(header.prop('viewType')).toBe(ETraceViewType.TraceGraph); expect(calculateTraceDagEVSpy).toHaveBeenCalledWith(defaultProps.trace.data); + onTraceViewChange(ETraceViewType.TraceSpansView); + wrapper.update(); + refreshWrappers(); + expect(header.prop('viewType')).toBe(ETraceViewType.TraceSpansView); + + onTraceViewChange(ETraceViewType.TraceStatistics); + wrapper.update(); + refreshWrappers(); + expect(header.prop('viewType')).toBe(ETraceViewType.TraceStatistics); wrapper.setProps({ trace: {} }); onTraceViewChange(ETraceViewType.TraceTimelineViewer); expect(calculateTraceDagEVSpy).toHaveBeenCalledTimes(1); diff --git a/packages/jaeger-ui/src/utils/date.tsx b/packages/jaeger-ui/src/utils/date.tsx index 39542d25eb..b5776278aa 100644 --- a/packages/jaeger-ui/src/utils/date.tsx +++ b/packages/jaeger-ui/src/utils/date.tsx @@ -144,3 +144,26 @@ export function formatRelativeDate(value: any, fullMonthName: boolean = false) { } return m.format(`${monthFormat} D`); } + +export function timeConversion(microseconds: number) { + const milliseconds: number = parseInt((microseconds / 1000).toFixed(2), 10); + const seconds: number = parseInt((milliseconds / 1000).toFixed(2), 10); + const minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1), 10); + const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1), 10); + const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1), 10); + let timeText; + if (microseconds < 1000) { + timeText = `${microseconds}μs`; + } else if (milliseconds < 1000) { + timeText = `${milliseconds}ms`; + } else if (seconds < 60) { + timeText = `${seconds}Sec`; + } else if (minutes < 60) { + timeText = `${minutes}Min`; + } else if (hours < 24) { + timeText = `${hours}Hrs`; + } else { + timeText = `${days}Days`; + } + return timeText; +} From c3fa88b852dae674a4ef787f8d8ebdc53a900851 Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 9 Aug 2021 23:51:57 +0530 Subject: [PATCH 07/10] unit test cases Signed-off-by: vvvprabhakar --- .../src/components/TracePage/TraceSpanView/index.test.js | 3 +-- .../jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js index 162072c885..07d0505328 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js @@ -57,7 +57,7 @@ describe('', () => { const event = ['op2', 'op3']; wrapper = shallow(); wrapper.find('.operationNameDD Select').simulate('change', event); - expect(wrapper.state('selectedOperationName')).toEqual( ['op2', 'op3']); + expect(wrapper.state('selectedOperationName')).toEqual(['op2', 'op3']); }); it('check handler', () => { const instance = wrapper.instance(); @@ -68,6 +68,5 @@ describe('', () => { expect(instance.state.operationNamesList).toBeDefined(); expect(instance.state.operationNamesList.length).toBe(6); expect(instance.state.operationNamesList).toEqual(['op1', 'op2', 'op3', 'op4', 'op6', 'op7']); - }); }); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index 72a1b3f892..59b0ceba95 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -16,7 +16,6 @@ import React, { Component } from 'react'; import { Row, Col, Table, Button, Select } from 'antd'; import moment from 'moment'; import { ColumnProps } from 'antd/es/table'; -import { SelectValue } from 'antd/lib/select'; import FormItem from 'antd/lib/form/FormItem'; import './index.css'; import { TNil } from '../../../types'; From 009f960ebad94fe9109483cd6c53e16c9c8f2c0e Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Wed, 18 Aug 2021 14:32:29 +0530 Subject: [PATCH 08/10] removed the commented line Signed-off-by: vvvprabhakar --- .../src/components/TracePage/TraceSpanView/index.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js index 07d0505328..0dde8c6fae 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js @@ -49,7 +49,6 @@ describe('', () => { it('Should change value when onChange was called', () => { const event = ['service2']; wrapper = shallow(); - // wrapper.state('filtered.process.serviceName', ['service1', 'service2']); wrapper.find('.serviceNameDD Select').simulate('change', event); expect(wrapper.state('selectedServiceName')).toEqual(['service2']); }); From 9a78bc55e84a3a095d80a8006380a2281950df26 Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 30 Aug 2021 00:48:55 +0530 Subject: [PATCH 09/10] unit test cases Signed-off-by: vvvprabhakar --- .../TracePage/TraceSpanView/index.test.js | 10 ++++- .../TracePage/TraceSpanView/index.tsx | 20 +--------- packages/jaeger-ui/src/utils/date.test.js | 37 ++++++++++++++++++- packages/jaeger-ui/src/utils/date.tsx | 6 +-- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js index 0dde8c6fae..130ebc4e9b 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.test.js @@ -42,7 +42,6 @@ describe('', () => { expect(wrapper.find('table').length).toBe(1); expect(wrapper.find('colgroup').length).toBe(1); expect(wrapper.find('Pagination').length).toBe(2); - expect(wrapper.find('Button').length).toBe(1); expect(wrapper.find('.ant-form-item-control').length).toBe(3); }); @@ -52,6 +51,14 @@ describe('', () => { wrapper.find('.serviceNameDD Select').simulate('change', event); expect(wrapper.state('selectedServiceName')).toEqual(['service2']); }); + it('Should change value when onChange and Rest the value when called reset', () => { + const event = ['service2']; + wrapper = shallow(); + wrapper.find('.serviceNameDD Select').simulate('change', event); + expect(wrapper.state('selectedServiceName')).toEqual(['service2']); + wrapper.find('.reset-filter Button').simulate('click'); + expect(wrapper.state('selectedServiceName')).toEqual([]); + }); it('Should change value when onChange OperatioName DDwas called', () => { const event = ['op2', 'op3']; wrapper = shallow(); @@ -60,7 +67,6 @@ describe('', () => { }); it('check handler', () => { const instance = wrapper.instance(); - expect(instance.state.serviceNamesList).toBeDefined(); expect(instance.state.serviceNamesList.length).toBe(2); expect(instance.state.serviceNamesList).toEqual(['service1', 'service2']); diff --git a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx index 59b0ceba95..c1145150d7 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceSpanView/index.tsx @@ -80,24 +80,6 @@ export default class TraceSpanView extends Component { this.uniqueOperationNameOptions = this.uniqueOperationNameOptions.bind(this); } - handleSearch(selectedKeys: string[], confirm: () => void, dataIndex: string): void { - confirm(); - this.setState(previousState => ({ - ...previousState, - searchText: selectedKeys[0], - searchedColumn: dataIndex, - })); - } - - handleReset(clearFilters: () => void) { - clearFilters(); - this.setState(previousState => ({ - ...previousState, - searchText: '', - data: this.props.trace.spans, - })); - } - handleResetFilter() { this.setState(previousState => ({ selectedServiceName: [], @@ -249,7 +231,7 @@ export default class TraceSpanView extends Component { - + diff --git a/packages/jaeger-ui/src/utils/date.test.js b/packages/jaeger-ui/src/utils/date.test.js index 64e54bb240..778f1b5dfb 100644 --- a/packages/jaeger-ui/src/utils/date.test.js +++ b/packages/jaeger-ui/src/utils/date.test.js @@ -12,7 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { formatDuration, ONE_MILLISECOND, ONE_SECOND, ONE_MINUTE, ONE_HOUR, ONE_DAY } from './date.tsx'; +import { + formatDuration, + timeConversion, + ONE_MILLISECOND, + ONE_SECOND, + ONE_MINUTE, + ONE_HOUR, + ONE_DAY, +} from './date.tsx'; describe('formatDuration', () => { it('keeps microseconds the same', () => { @@ -59,3 +67,30 @@ describe('formatDuration', () => { expect(formatDuration(input)).toBe('0μs'); }); }); + +describe('timeConversion', () => { + it('displays time in nanoseconds', () => { + const input = 999; + expect(timeConversion(input)).toBe('999μs'); + }); + it('displays time in milliseconds ', () => { + const input = 5000; + expect(timeConversion(input)).toBe('5ms'); + }); + it('displays time in seconds', () => { + const input = 5000000; + expect(timeConversion(input)).toBe('5Sec'); + }); + it('displays time in mintues', () => { + const input = 120000000; + expect(timeConversion(input)).toBe('2Min'); + }); + it('displays time in hours', () => { + const input = 7200000000; + expect(timeConversion(input)).toBe('2Hrs'); + }); + it('displays time in days', () => { + const input = 172800000000; + expect(timeConversion(input)).toBe('2Days'); + }); +}); diff --git a/packages/jaeger-ui/src/utils/date.tsx b/packages/jaeger-ui/src/utils/date.tsx index b5776278aa..16f6626652 100644 --- a/packages/jaeger-ui/src/utils/date.tsx +++ b/packages/jaeger-ui/src/utils/date.tsx @@ -148,9 +148,9 @@ export function formatRelativeDate(value: any, fullMonthName: boolean = false) { export function timeConversion(microseconds: number) { const milliseconds: number = parseInt((microseconds / 1000).toFixed(2), 10); const seconds: number = parseInt((milliseconds / 1000).toFixed(2), 10); - const minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(1), 10); - const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(1), 10); - const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(1), 10); + const minutes: number = parseInt((milliseconds / (1000 * 60)).toFixed(2), 10); + const hours: number = parseInt((milliseconds / (1000 * 60 * 60)).toFixed(2), 10); + const days: number = parseInt((milliseconds / (1000 * 60 * 60 * 24)).toFixed(2), 10); let timeText; if (microseconds < 1000) { timeText = `${microseconds}μs`; From 7328d6c57c37015cef6d13d3e169b1bca12ab8ee Mon Sep 17 00:00:00 2001 From: vvvprabhakar Date: Mon, 30 Aug 2021 11:16:00 +0530 Subject: [PATCH 10/10] unit test cases & removed unnecessary changes Signed-off-by: vvvprabhakar --- .../TraceStatistics/TraceStatisticsHeader.tsx | 48 +++---------------- 1 file changed, 7 insertions(+), 41 deletions(-) diff --git a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx index 65269e770b..9f78932398 100644 --- a/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx +++ b/packages/jaeger-ui/src/components/TracePage/TraceStatistics/TraceStatisticsHeader.tsx @@ -30,8 +30,7 @@ type Props = { tableValue: ITableSpan[], wholeTable: ITableSpan[], valueNameSelector1: string, - valueNameSelector2: string | null, - valueNameSelector4: string | null + valueNameSelector2: string | null ) => void; }; @@ -39,7 +38,7 @@ type State = { valueNameSelector1: string; valueNameSelector2: string | null; valueNameSelector3: string; - valueNameSelector4: string | null; + checkboxStatus: boolean; }; @@ -63,21 +62,18 @@ export default class TraceStatisticsHeader extends Component { getColumnValues('Service Name', this.props.trace), getColumnValues('Service Name', this.props.trace), 'Service Name', - null, null ); this.state = { valueNameSelector1: 'Service Name', valueNameSelector2: null, - valueNameSelector4: null, valueNameSelector3: 'Count', checkboxStatus: false, }; this.setValueNameSelector1 = this.setValueNameSelector1.bind(this); this.setValueNameSelector2 = this.setValueNameSelector2.bind(this); this.setValueNameSelector3 = this.setValueNameSelector3.bind(this); - this.setValueNameSelector4 = this.setValueNameSelector4.bind(this); this.checkboxButton = this.checkboxButton.bind(this); this.clearValue = this.clearValue.bind(this); } @@ -111,7 +107,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, value, null, null); + this.props.handler(newTableValue, newWohleTable, value, null); } /** @@ -141,7 +137,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value, null); + this.props.handler(newTableValue, newWohleTable, this.state.valueNameSelector1, value); } /** @@ -162,27 +158,7 @@ export default class TraceStatisticsHeader extends Component { newTableValue, newWohleTable, this.state.valueNameSelector1, - this.state.valueNameSelector2, - this.state.valueNameSelector4 - ); - } - - /** - * - */ - setValueNameSelector4(value: string) { - this.setState({ - valueNameSelector4: value, - }); - const newTableValue = generateColor(this.props.tableValue, this.getValue(), this.state.checkboxStatus); - const newWholeTable = generateColor(this.props.wholeTable, this.getValue(), this.state.checkboxStatus); - - this.props.handler( - newTableValue, - newWholeTable, - this.state.valueNameSelector1, - this.state.valueNameSelector2, - this.state.valueNameSelector4 + this.state.valueNameSelector2 ); } @@ -200,8 +176,7 @@ export default class TraceStatisticsHeader extends Component { newTableValue, newWholeTable, this.state.valueNameSelector1, - this.state.valueNameSelector2, - this.state.valueNameSelector4 + this.state.valueNameSelector2 ); } @@ -223,7 +198,7 @@ export default class TraceStatisticsHeader extends Component { this.getValue(), this.state.checkboxStatus ); - this.props.handler(newTableValue, newWholeTable, this.state.valueNameSelector1, null, null); + this.props.handler(newTableValue, newWholeTable, this.state.valueNameSelector1, null); } render() { @@ -253,15 +228,6 @@ export default class TraceStatisticsHeader extends Component { clearValue={this.clearValue} required={false} /> -