diff --git a/Common/Types/BaseDatabase/Includes.ts b/Common/Types/BaseDatabase/Includes.ts index 1fe7b1f05c..bfd0c3d6c3 100644 --- a/Common/Types/BaseDatabase/Includes.ts +++ b/Common/Types/BaseDatabase/Includes.ts @@ -31,11 +31,12 @@ export default class Includes extends SerializableObject { public static override fromJSON(json: JSONObject): Includes { if (json['_type'] === ObjectType.Includes) { + const valuesArray: Array = []; - const valuesArray: Array = []; - - for(const value of json['value'] as Array || []) { - valuesArray.push(JSONFunctions.deserializeValue(value) as string); + for (const value of (json['value'] as Array) || []) { + valuesArray.push( + JSONFunctions.deserializeValue(value) as string + ); } return new Includes(valuesArray); diff --git a/CommonServer/Utils/AnalyticsDatabase/Statement.ts b/CommonServer/Utils/AnalyticsDatabase/Statement.ts index b6196b7135..d3f864fd68 100644 --- a/CommonServer/Utils/AnalyticsDatabase/Statement.ts +++ b/CommonServer/Utils/AnalyticsDatabase/Statement.ts @@ -37,8 +37,8 @@ export class Statement implements BaseQueryParams { typeof param === 'string' ? 'Identifier' : Statement.toColumnType(param); - - return prev + `{p${i - 1}:${dataType}}` + curr; + + return prev + `{p${i - 1}:${dataType}}` + curr; } ); @@ -105,11 +105,13 @@ export class Statement implements BaseQueryParams { ) { finalValue = v.value.value; } else if (v.value instanceof Includes) { - if (v.type === TableColumnType.Text || v.type === TableColumnType.ObjectID) { - + if ( + v.type === TableColumnType.Text || + v.type === TableColumnType.ObjectID + ) { finalValue = v.value.values.map((val: string | ObjectID) => { return `${val.toString()}`; - }) + }); } else { finalValue = v.value.values; } @@ -152,7 +154,9 @@ export class Statement implements BaseQueryParams { })}`; } - private static toColumnType(statementParam: StatementParameter | string): string { + private static toColumnType( + statementParam: StatementParameter | string + ): string { // ensure we have a mapping for all types (a missing mapping will // be a compile error) const columnTypes: Dictionary = { @@ -169,11 +173,13 @@ export class Statement implements BaseQueryParams { [TableColumnType.LongNumber]: 'Int128', }; - if((statementParam as StatementParameter).value instanceof Includes){ + if ((statementParam as StatementParameter).value instanceof Includes) { return 'Array(String)'; } - return columnTypes[(statementParam as StatementParameter).type] || 'String'; + return ( + columnTypes[(statementParam as StatementParameter).type] || 'String' + ); } } diff --git a/CommonServer/Utils/AnalyticsDatabase/StatementGenerator.ts b/CommonServer/Utils/AnalyticsDatabase/StatementGenerator.ts index 0ebe210c8f..2f0bda11c0 100644 --- a/CommonServer/Utils/AnalyticsDatabase/StatementGenerator.ts +++ b/CommonServer/Utils/AnalyticsDatabase/StatementGenerator.ts @@ -405,8 +405,6 @@ export default class StatementGenerator { } } - - return whereStatement; } diff --git a/CommonUI/src/Components/GanttChart/Bar/Index.tsx b/CommonUI/src/Components/GanttChart/Bar/Index.tsx index 1eeb24daba..5b29f1c8bb 100644 --- a/CommonUI/src/Components/GanttChart/Bar/Index.tsx +++ b/CommonUI/src/Components/GanttChart/Bar/Index.tsx @@ -23,6 +23,9 @@ export interface ComponentProps { chartTimelineStart: number; chartTimelineEnd: number; timelineWidth: number; + areOtherBarsSelected: boolean; + onSelect: (barId: string) => void; + onDeselect: (barId: string) => void; } const Bar: FunctionComponent = ( @@ -30,6 +33,8 @@ const Bar: FunctionComponent = ( ): ReactElement => { const [isHovered, setIsHovered] = useState(false); + const [isSelected, setIsSelected] = useState(false); + // calculate bar width. let barWidth: number = ((props.bar.barTimelineEnd - props.bar.barTimelineStart) / @@ -61,12 +66,20 @@ const Bar: FunctionComponent = ( setIsHovered(false); }; + + let barOpacity: number = 1; + + if(props.areOtherBarsSelected && !isSelected) { + barOpacity = 0.5; + } + return ( // rectangle div with curved corners and text inside in tailwindcss
= ( marginLeft: `${barLeftPosition}px`, width: `${barWidth}px`, backgroundColor: `${props.bar.barColor.toString()}`, + opacity: barOpacity, }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} + onClick={() => { + if (isSelected) { + props.onDeselect(props.bar.id); + } else { + props.onSelect(props.bar.id); + } + setIsSelected(!isSelected); + }} > {!showLabelOutsideBar && ( = ( className="h-8 pt-1 pb-1 mt-2.5 mb-2.5" style={{ marginLeft: `${barLeftPosition + barWidth + 10}px`, + opacity: barOpacity, }} > void; } export interface ComponentProps { @@ -21,6 +22,8 @@ const GanttChart: FunctionComponent = ( ): ReactElement => { const eachIntervalDefaultWidth: number = 100; // in pixels + const [selectedBarIds, setSelectedBarIds] = React.useState([]); + const [chartWidth, setChartWidth] = React.useState(0); const [timelineWidth, setTimelineWidth] = React.useState(2000); @@ -70,6 +73,11 @@ const GanttChart: FunctionComponent = ( chartTimelineStart={props.chart.timeline.start} rows={props.chart.rows} bars={props.chart.bars} + selectedBarIds={selectedBarIds} + onBarSelectChange={(barIds: string[]) => { + setSelectedBarIds(barIds); + props.chart.onBarSelectChange(barIds); + }} />
diff --git a/CommonUI/src/Components/GanttChart/Row/Index.tsx b/CommonUI/src/Components/GanttChart/Row/Index.tsx index 42ba6dc74e..8d29310d6b 100644 --- a/CommonUI/src/Components/GanttChart/Row/Index.tsx +++ b/CommonUI/src/Components/GanttChart/Row/Index.tsx @@ -15,6 +15,8 @@ export interface ComponentProps { chartTimelineStart: number; chartTimelineEnd: number; timelineWidth: number; + selectedBarIds: string[]; + onBarSelectChange: (barIds: string[]) => void; } const Row: FunctionComponent = ( @@ -43,6 +45,28 @@ const Row: FunctionComponent = ( chartTimelineEnd={props.chartTimelineEnd} chartTimelineStart={props.chartTimelineStart} timelineWidth={props.timelineWidth} + areOtherBarsSelected={props.selectedBarIds.length > 0} + onSelect={(barId: string) => { + // check if the bar is already selected + if (props.selectedBarIds.includes(barId)) { + return; + } + + props.onBarSelectChange([...props.selectedBarIds, barId]); + }} + onDeselect={(barId: string) => { + + // check if the bar is already selected + if (!props.selectedBarIds.includes(barId)) { + return; + } + + props.onBarSelectChange( + props.selectedBarIds.filter((id: string) => { + return id !== barId; + }) + ); + }} /> ); })} diff --git a/CommonUI/src/Components/GanttChart/Rows.tsx b/CommonUI/src/Components/GanttChart/Rows.tsx index 3b99442b7b..062852b43e 100644 --- a/CommonUI/src/Components/GanttChart/Rows.tsx +++ b/CommonUI/src/Components/GanttChart/Rows.tsx @@ -8,6 +8,8 @@ export interface ComponentProps { chartTimelineStart: number; chartTimelineEnd: number; timelineWidth: number; + selectedBarIds: string[]; + onBarSelectChange: (barIds: string[]) => void; } const Rows: FunctionComponent = ( @@ -27,6 +29,8 @@ const Rows: FunctionComponent = ( bars={props.bars.filter((bar: GanttChartBar) => { return bar.rowId === row.id; })} + selectedBarIds={props.selectedBarIds} + onBarSelectChange={props.onBarSelectChange} /> ); })} diff --git a/Dashboard/src/Pages/Telemetry/Services/View/Traces/View/Index.tsx b/Dashboard/src/Pages/Telemetry/Services/View/Traces/View/Index.tsx index 19308ed124..9e3d8f5c88 100644 --- a/Dashboard/src/Pages/Telemetry/Services/View/Traces/View/Index.tsx +++ b/Dashboard/src/Pages/Telemetry/Services/View/Traces/View/Index.tsx @@ -22,6 +22,9 @@ import DashboardLogsViewer from '../../../../../../Components/LogsViewer/LogsVie const TraceView: FunctionComponent = ( _props: PageComponentProps ): ReactElement => { + + const [selectedSpans, setSelectedSpans] = React.useState([]); + const oneuptimeSpanId: ObjectID = Navigation.getLastParamAsObjectID(0); const telemetryServiceId: ObjectID = Navigation.getLastParamAsObjectID(0); @@ -255,6 +258,9 @@ const TraceView: FunctionComponent = ( description: span.spanId!, }; }), + onBarSelectChange(barIds: Array) { + setSelectedSpans(barIds); + }, bars: spans.map((span: Span) => { const spanColor: { barColor: Color; @@ -319,6 +325,7 @@ const TraceView: FunctionComponent = ( noLogsMessage="No logs found for this trace." telemetryServiceIds={[telemetryServiceId]} traceIds={[traceId]} + spanIds={selectedSpans} enableRealtime={false} /> )} diff --git a/Ingestor/API/OTelIngest.ts b/Ingestor/API/OTelIngest.ts index 39ecbed29d..a6fcc6ab8c 100644 --- a/Ingestor/API/OTelIngest.ts +++ b/Ingestor/API/OTelIngest.ts @@ -140,9 +140,9 @@ router.post( span['endTimeUnixNano'] as number ); - dbSpan.durationUnixNano = span[ - 'endTimeUnixNano' - ] as number - (span['startTimeUnixNano'] as number); + dbSpan.durationUnixNano = + (span['endTimeUnixNano'] as number) - + (span['startTimeUnixNano'] as number); dbSpan.name = span['name'] as string; dbSpan.kind = diff --git a/Model/AnalyticsModels/Span.ts b/Model/AnalyticsModels/Span.ts index 1b99d7a65d..0db1034eaf 100644 --- a/Model/AnalyticsModels/Span.ts +++ b/Model/AnalyticsModels/Span.ts @@ -267,7 +267,6 @@ export default class Span extends AnalyticsBaseModel { }, }), - new AnalyticsTableColumn({ key: 'durationUnixNano', title: 'Duration in Unix Nano', @@ -588,7 +587,6 @@ export default class Span extends AnalyticsBaseModel { this.setColumnValue('startTimeUnixNano', v); } - public get durationUnixNano(): number | undefined { return this.getColumnValue('durationUnixNano') as number | undefined; } @@ -653,8 +651,6 @@ export default class Span extends AnalyticsBaseModel { this.setColumnValue('endTime', v); } - - public get traceId(): string | undefined { return this.getColumnValue('traceId') as string | undefined; }