From bf7fae4bd322c977c13a6751aeab21bf5e5e800b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 11 Oct 2023 18:04:54 +0200 Subject: [PATCH] Tempo: Fix type errors that appeared when removing the "any" type from DataQueryResponseData (#75600) * Tempo: Improving typing of data types * last fix * Fix --- .../testfiles/snapshotable_dashboard.json | 4 +- .../testfiles/snapshotable_with_rows.json | 836 +++++++++--------- .../datasource/tempo/datasource.test.ts | 74 +- .../plugins/datasource/tempo/datasource.ts | 56 +- .../datasource/tempo/graphTransform.ts | 44 +- .../datasource/tempo/metricsSummary.ts | 1 + .../datasource/tempo/resultTransformer.ts | 69 +- .../app/plugins/datasource/tempo/variables.ts | 4 +- .../datasource/zipkin/utils/transforms.ts | 20 +- 9 files changed, 580 insertions(+), 528 deletions(-) diff --git a/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_dashboard.json b/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_dashboard.json index 3d4528fc997..b3fee37e368 100644 --- a/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_dashboard.json +++ b/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_dashboard.json @@ -193,9 +193,7 @@ "footer": { "countRows": false, "fields": "", - "reducer": [ - "sum" - ], + "reducer": ["sum"], "show": false }, "showHeader": true diff --git a/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_with_rows.json b/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_with_rows.json index 408c5fc18cd..a2fbce92e55 100644 --- a/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_with_rows.json +++ b/public/app/features/dashboard-scene/serialization/testfiles/snapshotable_with_rows.json @@ -1,425 +1,425 @@ { - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 2312, - "links": [], - "liveNow": false, - "panels": [ + "annotations": { + "list": [ { + "builtIn": 1, "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" + "type": "grafana", + "uid": "-- Grafana --" }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 3, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "refId": "A", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "Panel 1", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 2, - "panels": [], - "title": "Expanded row", - "type": "row" - }, - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "id": 1, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "refId": "B", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "Panel 2", - "type": "timeseries" - }, - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "refId": "C", - "scenarioId": "random_walk", - "seriesCount": 1 - } - ], - "title": "Panel 3", - "type": "timeseries" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 17 - }, - "id": 4, - "panels": [ - { - "datasource": { - "type": "grafana-testdata-datasource", - "uid": "gdev-testdata" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "mode": "single", - "sort": "none" - } - }, - "title": "Panel inside colapsed row", - "type": "timeseries" - } - ], - "title": "Collapsed row", - "type": "row" + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" } - ], - "refresh": "", - "schemaVersion": 38, - "tags": [], - "templating": { - "list": [] + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2312, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "Panel 1", + "type": "timeseries" }, - "time": { - "from": "now-6h", - "to": "now" + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 2, + "panels": [], + "title": "Expanded row", + "type": "row" }, - "timepicker": {}, - "timezone": "", - "title": "Snapshots - basic rows", - "uid": "a3b23921-287d-4f90-9a14-c04228057dfa", - "version": 4, - "weekStart": "" - } + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "refId": "B", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "Panel 2", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "refId": "C", + "scenarioId": "random_walk", + "seriesCount": 1 + } + ], + "title": "Panel 3", + "type": "timeseries" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 4, + "panels": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "gdev-testdata" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Panel inside colapsed row", + "type": "timeseries" + } + ], + "title": "Collapsed row", + "type": "row" + } + ], + "refresh": "", + "schemaVersion": 38, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Snapshots - basic rows", + "uid": "a3b23921-287d-4f90-9a14-c04228057dfa", + "version": 4, + "weekStart": "" +} diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index 9091e9f61d3..13051aa8534 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -232,11 +232,11 @@ describe('Tempo data source', () => { expect(response.data).toHaveLength(2); const nodesFrame = response.data[0]; expect(nodesFrame.name).toBe('Nodes'); - expect(nodesFrame.meta.preferredVisualisationType).toBe('nodeGraph'); + expect(nodesFrame.meta?.preferredVisualisationType).toBe('nodeGraph'); const edgesFrame = response.data[1]; expect(edgesFrame.name).toBe('Edges'); - expect(edgesFrame.meta.preferredVisualisationType).toBe('nodeGraph'); + expect(edgesFrame.meta?.preferredVisualisationType).toBe('nodeGraph'); }); it('should build search query correctly', () => { @@ -486,67 +486,67 @@ describe('Tempo service graph view', () => { expect(response.data[0].fields[1].values.length).toBe(2); expect(response.data[0].fields[1].values[0]).toBe(12.75164671814457); expect(response.data[0].fields[1].values[1]).toBe(12.121331111401608); - expect(response.data[0].fields[1].config.decimals).toBe(2); - expect(response.data[0].fields[1].config.links[0].title).toBe('Rate'); - expect(response.data[0].fields[1].config.links[0].internal.query.expr).toBe( + expect(response.data[0].fields[1]?.config?.decimals).toBe(2); + expect(response.data[0].fields[1]?.config?.links?.[0]?.title).toBe('Rate'); + expect(response.data[0].fields[1]?.config?.links?.[0]?.internal?.query.expr).toBe( 'sum(rate(traces_spanmetrics_calls_total{span_name="${__data.fields[0]}"}[$__rate_interval]))' ); - expect(response.data[0].fields[1].config.links[0].internal.query.range).toBe(true); - expect(response.data[0].fields[1].config.links[0].internal.query.exemplar).toBe(true); - expect(response.data[0].fields[1].config.links[0].internal.query.instant).toBe(false); + expect(response.data[0].fields[1]?.config?.links?.[0]?.internal?.query.range).toBe(true); + expect(response.data[0].fields[1]?.config?.links?.[0]?.internal?.query.exemplar).toBe(true); + expect(response.data[0].fields[1]?.config?.links?.[0]?.internal?.query.instant).toBe(false); expect(response.data[0].fields[2].values.length).toBe(2); expect(response.data[0].fields[2].values[0]).toBe(12.75164671814457); expect(response.data[0].fields[2].values[1]).toBe(12.121331111401608); - expect(response.data[0].fields[2].config.color.mode).toBe('continuous-BlPu'); - expect(response.data[0].fields[2].config.custom.cellOptions.mode).toBe(BarGaugeDisplayMode.Lcd); - expect(response.data[0].fields[2].config.custom.cellOptions.type).toBe(TableCellDisplayMode.Gauge); - expect(response.data[0].fields[2].config.decimals).toBe(3); + expect(response.data[0].fields[2]?.config?.color?.mode).toBe('continuous-BlPu'); + expect(response.data[0].fields[2]?.config?.custom.cellOptions.mode).toBe(BarGaugeDisplayMode.Lcd); + expect(response.data[0].fields[2]?.config?.custom.cellOptions.type).toBe(TableCellDisplayMode.Gauge); + expect(response.data[0].fields[2]?.config?.decimals).toBe(3); expect(response.data[0].fields[3].name).toBe('Error Rate'); expect(response.data[0].fields[3].values.length).toBe(2); expect(response.data[0].fields[3].values[0]).toBe(3.75164671814457); expect(response.data[0].fields[3].values[1]).toBe(3.121331111401608); - expect(response.data[0].fields[3].config.decimals).toBe(2); - expect(response.data[0].fields[3].config.links[0].title).toBe('Error Rate'); - expect(response.data[0].fields[3].config.links[0].internal.query.expr).toBe( + expect(response.data[0].fields[3]?.config?.decimals).toBe(2); + expect(response.data[0].fields[3]?.config?.links?.[0]?.title).toBe('Error Rate'); + expect(response.data[0].fields[3]?.config?.links?.[0]?.internal?.query.expr).toBe( 'sum(rate(traces_spanmetrics_calls_total{status_code="STATUS_CODE_ERROR",span_name="${__data.fields[0]}"}[$__rate_interval]))' ); - expect(response.data[0].fields[3].config.links[0].internal.query.range).toBe(true); - expect(response.data[0].fields[3].config.links[0].internal.query.exemplar).toBe(true); - expect(response.data[0].fields[3].config.links[0].internal.query.instant).toBe(false); + expect(response.data[0].fields[3]?.config?.links?.[0]?.internal?.query.range).toBe(true); + expect(response.data[0].fields[3]?.config?.links?.[0]?.internal?.query.exemplar).toBe(true); + expect(response.data[0].fields[3]?.config?.links?.[0]?.internal?.query.instant).toBe(false); expect(response.data[0].fields[4].values.length).toBe(2); expect(response.data[0].fields[4].values[0]).toBe(3.75164671814457); expect(response.data[0].fields[4].values[1]).toBe(3.121331111401608); - expect(response.data[0].fields[4].config.color.mode).toBe('continuous-RdYlGr'); - expect(response.data[0].fields[4].config.custom.cellOptions.mode).toBe(BarGaugeDisplayMode.Lcd); - expect(response.data[0].fields[4].config.custom.cellOptions.type).toBe(TableCellDisplayMode.Gauge); - expect(response.data[0].fields[4].config.decimals).toBe(3); + expect(response.data[0].fields[4]?.config?.color?.mode).toBe('continuous-RdYlGr'); + expect(response.data[0].fields[4]?.config?.custom.cellOptions.mode).toBe(BarGaugeDisplayMode.Lcd); + expect(response.data[0].fields[4]?.config?.custom.cellOptions.type).toBe(TableCellDisplayMode.Gauge); + expect(response.data[0].fields[4]?.config?.decimals).toBe(3); expect(response.data[0].fields[5].name).toBe('Duration (p90)'); expect(response.data[0].fields[5].values.length).toBe(2); expect(response.data[0].fields[5].values[0]).toBe('0'); expect(response.data[0].fields[5].values[1]).toBe(0.12003505696757232); - expect(response.data[0].fields[5].config.unit).toBe('s'); - expect(response.data[0].fields[5].config.links[0].title).toBe('Duration'); - expect(response.data[0].fields[5].config.links[0].internal.query.expr).toBe( + expect(response.data[0].fields[5]?.config?.unit).toBe('s'); + expect(response.data[0].fields[5]?.config?.links?.[0]?.title).toBe('Duration'); + expect(response.data[0].fields[5]?.config?.links?.[0]?.internal?.query.expr).toBe( 'histogram_quantile(.9, sum(rate(traces_spanmetrics_latency_bucket{span_name="${__data.fields[0]}"}[$__rate_interval])) by (le))' ); - expect(response.data[0].fields[5].config.links[0].internal.query.range).toBe(true); - expect(response.data[0].fields[5].config.links[0].internal.query.exemplar).toBe(true); - expect(response.data[0].fields[5].config.links[0].internal.query.instant).toBe(false); + expect(response.data[0].fields[5]?.config?.links?.[0]?.internal?.query.range).toBe(true); + expect(response.data[0].fields[5]?.config?.links?.[0]?.internal?.query.exemplar).toBe(true); + expect(response.data[0].fields[5]?.config?.links?.[0]?.internal?.query.instant).toBe(false); - expect(response.data[0].fields[6].config.links[0].url).toBe(''); - expect(response.data[0].fields[6].config.links[0].title).toBe('Tempo'); - expect(response.data[0].fields[6].config.links[0].internal.query.queryType).toBe('traceqlSearch'); - expect(response.data[0].fields[6].config.links[0].internal.query.filters[0].value).toBe('${__data.fields[0]}'); + expect(response.data[0].fields[6]?.config?.links?.[0].url).toBe(''); + expect(response.data[0].fields[6]?.config?.links?.[0].title).toBe('Tempo'); + expect(response.data[0].fields[6]?.config?.links?.[0].internal.query.queryType).toBe('traceqlSearch'); + expect(response.data[0].fields[6]?.config?.links?.[0].internal.query.filters[0].value).toBe('${__data.fields[0]}'); // Service graph expect(response.data[1].name).toBe('Nodes'); expect(response.data[1].fields[0].values.length).toBe(3); - expect(response.data[1].fields[0].config.links.length).toBeGreaterThan(0); - expect(response.data[1].fields[0].config.links).toEqual(serviceGraphLinks); + expect(response.data[1].fields[0]?.config?.links?.length).toBeGreaterThan(0); + expect(response.data[1].fields[0]?.config?.links).toEqual(serviceGraphLinks); expect(response.data[2].name).toBe('Edges'); expect(response.data[2].fields[0].values.length).toBe(2); @@ -804,7 +804,7 @@ describe('Tempo service graph view', () => { fields: [ { name: 'Time', - type: 'time', + type: FieldType.time, config: {}, values: [1653828275000, 1653828275000, 1653828275000, 1653828275000, 1653828275000], }, @@ -813,12 +813,14 @@ describe('Tempo service graph view', () => { config: { filterable: true, }, - type: 'string', + type: FieldType.string, values: ['HTTP Client', 'HTTP GET', 'HTTP GET - root', 'HTTP POST', 'HTTP POST - post'], }, ], + values: [], }, ]; + const objToAlign = { 'HTTP GET - root': { value: '0.1234', diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index b9d40dbb218..f539a54c3dd 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -6,13 +6,13 @@ import semver from 'semver'; import { CoreApp, DataFrame, + DataFrameDTO, DataQueryRequest, DataQueryResponse, DataQueryResponseData, DataSourceApi, DataSourceInstanceSettings, dateTime, - Field, FieldType, isValidGoDuration, LoadingState, @@ -86,6 +86,17 @@ const featuresToTempoVersion = { // This is the last minor version of Tempo that does not expose the endpoint for build information. const defaultTempoVersion = '2.1.0'; +interface ServiceMapQueryResponse { + nodes: DataFrame; + edges: DataFrame; +} + +interface ServiceMapQueryResponseWithRates { + rates: Array; + nodes: DataFrame; + edges: DataFrame; +} + export class TempoDatasource extends DataSourceWithBackend { tracesToLogs?: TraceToLogsOptions; serviceMap?: { @@ -783,7 +794,11 @@ function queryPrometheus(request: DataQueryRequest, datasourceUid: st ); } -function serviceMapQuery(request: DataQueryRequest, datasourceUid: string, tempoDatasourceUid: string) { +function serviceMapQuery( + request: DataQueryRequest, + datasourceUid: string, + tempoDatasourceUid: string +): Observable { const serviceMapRequest = makePromServiceMapRequest(request); return queryPrometheus(serviceMapRequest, datasourceUid).pipe( @@ -849,7 +864,8 @@ function serviceMapQuery(request: DataQueryRequest, datasourceUid: s } return { - data: [nodes, edges], + nodes, + edges, state: LoadingState.Done, }; }) @@ -858,9 +874,9 @@ function serviceMapQuery(request: DataQueryRequest, datasourceUid: s function rateQuery( request: DataQueryRequest, - serviceMapResponse: DataQueryResponse, + serviceMapResponse: ServiceMapQueryResponse, datasourceUid: string -) { +): Observable { const serviceMapRequest = makePromServiceMapRequest(request); serviceMapRequest.targets = makeServiceGraphViewRequest([buildExpr(rateMetric, defaultTableFilter, request)]); @@ -872,8 +888,9 @@ function rateQuery( throw new Error(getErrorMessage(errorRes.error?.message)); } return { - data: [responses[0]?.data ?? [], serviceMapResponse.data[0], serviceMapResponse.data[1]], - state: LoadingState.Done, + rates: responses[0]?.data ?? [], + nodes: serviceMapResponse.nodes, + edges: serviceMapResponse.edges, }; }) ); @@ -883,7 +900,7 @@ function rateQuery( // -> which determine the errorRate/duration span_name(s) we need to query function errorAndDurationQuery( request: DataQueryRequest, - rateResponse: DataQueryResponse, + rateResponse: ServiceMapQueryResponseWithRates, datasourceUid: string, tempoDatasourceUid: string ) { @@ -892,14 +909,14 @@ function errorAndDurationQuery( let durationsBySpanName: string[] = []; let labels = []; - if (rateResponse.data[0][0] && request.app === CoreApp.Explore) { - const spanNameField = rateResponse.data[0][0].fields.find((field: Field) => field.name === 'span_name'); + if (rateResponse.rates[0] && request.app === CoreApp.Explore) { + const spanNameField = rateResponse.rates[0].fields.find((field) => field.name === 'span_name'); if (spanNameField && spanNameField.values) { labels = spanNameField.values; } - } else if (rateResponse.data[0]) { - rateResponse.data[0].map((df: DataFrame) => { - const spanNameLabels = df.fields.find((field: Field) => field.labels?.['span_name']); + } else if (rateResponse.rates) { + rateResponse.rates.map((df: DataFrame | DataFrameDTO) => { + const spanNameLabels = df.fields.find((field) => field.labels?.['span_name']); if (spanNameLabels) { labels.push(spanNameLabels.labels?.['span_name']); } @@ -941,13 +958,13 @@ function errorAndDurationQuery( if (serviceGraphView.fields.length === 0) { return { - data: [rateResponse.data[1], rateResponse.data[2]], + data: [rateResponse.nodes, rateResponse.edges], state: LoadingState.Done, }; } return { - data: [serviceGraphView, rateResponse.data[1], rateResponse.data[2]], + data: [serviceGraphView, rateResponse.nodes, rateResponse.edges], state: LoadingState.Done, }; }) @@ -1078,7 +1095,7 @@ function makePromServiceMapRequest(options: DataQueryRequest): DataQ function getServiceGraphView( request: DataQueryRequest, - rateResponse: DataQueryResponse, + rateResponse: ServiceMapQueryResponseWithRates, secondResponse: DataQueryResponse, errorRateBySpanName: string, durationsBySpanName: string[], @@ -1086,14 +1103,15 @@ function getServiceGraphView( tempoDatasourceUid: string ) { let df: any = { fields: [] }; - const rate = rateResponse.data[0]?.filter((x: { refId: string }) => { + + const rate = rateResponse.rates.filter((x) => { return x.refId === buildExpr(rateMetric, defaultTableFilter, request); }); const errorRate = secondResponse.data.filter((x) => { return x.refId === errorRateBySpanName; }); const duration = secondResponse.data.filter((x) => { - return durationsBySpanName.includes(x.refId); + return durationsBySpanName.includes(x.refId ?? ''); }); if (rate.length > 0 && rate[0].fields?.length > 2) { @@ -1193,7 +1211,7 @@ function getServiceGraphView( if (d.fields.length > 1) { const delimiter = d.refId?.includes('span_name=~"') ? 'span_name=~"' : 'span_name="'; const name = d.refId?.split(delimiter)[1].split('"}')[0]; - durationObj[name] = { value: d.fields[1].values[0] }; + durationObj[name!] = { value: d.fields[1].values[0] }; } }); if (Object.keys(durationObj).length > 0) { diff --git a/public/app/plugins/datasource/tempo/graphTransform.ts b/public/app/plugins/datasource/tempo/graphTransform.ts index 181225e4351..4b7b4b56fed 100644 --- a/public/app/plugins/datasource/tempo/graphTransform.ts +++ b/public/app/plugins/datasource/tempo/graphTransform.ts @@ -8,6 +8,7 @@ import { NodeGraphDataFrameFieldNames as Fields, TimeRange, FieldType, + toDataFrame, } from '@grafana/data'; import { getNonOverlappingDuration, getStats, makeFrames, makeSpanMap } from '../../../core/utils/tracing'; @@ -189,39 +190,53 @@ function createServiceMapDataFrames() { } const nodes = createDF('Nodes', [ - { name: Fields.id, type: FieldType.string }, - { name: Fields.title, type: FieldType.string, config: { displayName: 'Service name' } }, - { name: Fields.subTitle, type: FieldType.string, config: { displayName: 'Service namespace' } }, - { name: Fields.mainStat, type: FieldType.number, config: { unit: 'ms/r', displayName: 'Average response time' } }, + { name: Fields.id, type: FieldType.string, values: [] }, + { name: Fields.title, type: FieldType.string, config: { displayName: 'Service name' }, values: [] }, + { name: Fields.subTitle, type: FieldType.string, config: { displayName: 'Service namespace' }, values: [] }, + { + name: Fields.mainStat, + type: FieldType.number, + config: { unit: 'ms/r', displayName: 'Average response time' }, + values: [], + }, { name: Fields.secondaryStat, type: FieldType.number, config: { unit: 'r/sec', displayName: 'Requests per second' }, + values: [], }, { name: Fields.arc + 'success', type: FieldType.number, config: { displayName: 'Success', color: { fixedColor: 'green', mode: FieldColorModeId.Fixed } }, + values: [], }, { name: Fields.arc + 'failed', type: FieldType.number, config: { displayName: 'Failed', color: { fixedColor: 'red', mode: FieldColorModeId.Fixed } }, + values: [], }, ]); const edges = createDF('Edges', [ - { name: Fields.id, type: FieldType.string }, - { name: Fields.source, type: FieldType.string }, - { name: AdditionalEdgeFields.sourceName, type: FieldType.string }, - { name: AdditionalEdgeFields.sourceNamespace, type: FieldType.string }, - { name: Fields.target, type: FieldType.string }, - { name: AdditionalEdgeFields.targetName, type: FieldType.string }, - { name: AdditionalEdgeFields.targetNamespace, type: FieldType.string }, - { name: Fields.mainStat, type: FieldType.number, config: { unit: 'ms/r', displayName: 'Average response time' } }, + { name: Fields.id, type: FieldType.string, values: [] }, + { name: Fields.source, type: FieldType.string, values: [] }, + { name: AdditionalEdgeFields.sourceName, type: FieldType.string, values: [] }, + { name: AdditionalEdgeFields.sourceNamespace, type: FieldType.string, values: [] }, + { name: Fields.target, type: FieldType.string, values: [] }, + { name: AdditionalEdgeFields.targetName, type: FieldType.string, values: [] }, + { name: AdditionalEdgeFields.targetNamespace, type: FieldType.string, values: [] }, + { + name: Fields.mainStat, + type: FieldType.number, + config: { unit: 'ms/r', displayName: 'Average response time' }, + values: [], + }, { name: Fields.secondaryStat, type: FieldType.number, config: { unit: 'r/sec', displayName: 'Requests per second' }, + values: [], }, ]); @@ -234,8 +249,9 @@ function createServiceMapDataFrames() { * @param responses */ function getMetricFrames(responses: DataQueryResponse[]): Record { - return responses[0].data.reduce>((acc, frame) => { - acc[frame.refId] = new DataFrameView(frame); + return responses[0].data.reduce>((acc, frameDTO) => { + const frame = toDataFrame(frameDTO); + acc[frame.refId ?? 'A'] = new DataFrameView(frame); return acc; }, {}); } diff --git a/public/app/plugins/datasource/tempo/metricsSummary.ts b/public/app/plugins/datasource/tempo/metricsSummary.ts index 68898a8d840..e0b9c4ea23b 100644 --- a/public/app/plugins/datasource/tempo/metricsSummary.ts +++ b/public/app/plugins/datasource/tempo/metricsSummary.ts @@ -60,6 +60,7 @@ export function createTableFrameFromMetricsSummaryQuery( name: `${series.key}`, type: FieldType.string, config: getConfig(series, configQuery, instanceSettings), + values: [], }; }); }); diff --git a/public/app/plugins/datasource/tempo/resultTransformer.ts b/public/app/plugins/datasource/tempo/resultTransformer.ts index 08c86e59cdc..e63cc21bc78 100644 --- a/public/app/plugins/datasource/tempo/resultTransformer.ts +++ b/public/app/plugins/datasource/tempo/resultTransformer.ts @@ -16,6 +16,8 @@ import { createDataFrame, getDisplayProcessor, createTheme, + DataFrameDTO, + toDataFrame, } from '@grafana/data'; import { SearchTableType } from './dataquery.gen'; @@ -23,7 +25,7 @@ import { createGraphFrames } from './graphTransform'; import { Span, SpanAttributes, Spanset, TraceSearchMetadata } from './types'; export function createTableFrame( - logsFrame: DataFrame, + logsFrame: DataFrame | DataFrameDTO, datasourceUid: string, datasourceName: string, traceRegexs: string[] @@ -38,6 +40,7 @@ export function createTableFrame( width: 200, }, }, + values: [], }, { name: 'traceID', @@ -59,10 +62,12 @@ export function createTableFrame( }, ], }, + values: [], }, { name: 'Message', type: FieldType.string, + values: [], }, ], meta: { @@ -80,7 +85,7 @@ export function createTableFrame( for (let field of logsFrame.fields) { let hasMatch = false; if (field.type === FieldType.string) { - const values = field.values; + const values = field.values!; for (let i = 0; i < values.length; i++) { const line = values[i]; if (line) { @@ -88,7 +93,7 @@ export function createTableFrame( const match = line.match(traceRegex); if (match) { const traceId = match[1]; - const time = timeField ? timeField.values[i] : null; + const time = timeField ? timeField.values![i] : null; tableFrame.fields[0].values.push(time); tableFrame.fields[1].values.push(traceId); tableFrame.fields[2].values.push(line); @@ -226,23 +231,23 @@ export function transformFromOTLP( ): DataQueryResponse { const frame = new MutableDataFrame({ fields: [ - { name: 'traceID', type: FieldType.string }, - { name: 'spanID', type: FieldType.string }, - { name: 'parentSpanID', type: FieldType.string }, - { name: 'operationName', type: FieldType.string }, - { name: 'serviceName', type: FieldType.string }, - { name: 'kind', type: FieldType.string }, - { name: 'statusCode', type: FieldType.number }, - { name: 'statusMessage', type: FieldType.string }, - { name: 'instrumentationLibraryName', type: FieldType.string }, - { name: 'instrumentationLibraryVersion', type: FieldType.string }, - { name: 'traceState', type: FieldType.string }, - { name: 'serviceTags', type: FieldType.other }, - { name: 'startTime', type: FieldType.number }, - { name: 'duration', type: FieldType.number }, - { name: 'logs', type: FieldType.other }, - { name: 'references', type: FieldType.other }, - { name: 'tags', type: FieldType.other }, + { name: 'traceID', type: FieldType.string, values: [] }, + { name: 'spanID', type: FieldType.string, values: [] }, + { name: 'parentSpanID', type: FieldType.string, values: [] }, + { name: 'operationName', type: FieldType.string, values: [] }, + { name: 'serviceName', type: FieldType.string, values: [] }, + { name: 'kind', type: FieldType.string, values: [] }, + { name: 'statusCode', type: FieldType.number, values: [] }, + { name: 'statusMessage', type: FieldType.string, values: [] }, + { name: 'instrumentationLibraryName', type: FieldType.string, values: [] }, + { name: 'instrumentationLibraryVersion', type: FieldType.string, values: [] }, + { name: 'traceState', type: FieldType.string, values: [] }, + { name: 'serviceTags', type: FieldType.other, values: [] }, + { name: 'startTime', type: FieldType.number, values: [] }, + { name: 'duration', type: FieldType.number, values: [] }, + { name: 'logs', type: FieldType.other, values: [] }, + { name: 'references', type: FieldType.other, values: [] }, + { name: 'tags', type: FieldType.other, values: [] }, ], meta: { preferredVisualisationType: 'trace', @@ -487,7 +492,7 @@ function getOTLPReferences( } export function transformTrace(response: DataQueryResponse, nodeGraph = false): DataQueryResponse { - const frame: DataFrame = response.data[0]; + const frame = response.data[0]; if (!frame) { return emptyDataQueryResponse; @@ -495,7 +500,7 @@ export function transformTrace(response: DataQueryResponse, nodeGraph = false): let data = [...response.data]; if (nodeGraph) { - data.push(...createGraphFrames(frame)); + data.push(...createGraphFrames(toDataFrame(frame))); } return { @@ -512,6 +517,7 @@ export function createTableFrameFromSearch(data: TraceSearchMetadata[], instance { name: 'traceID', type: FieldType.string, + values: [], config: { unit: 'string', displayNameFromDS: 'Trace ID', @@ -531,10 +537,15 @@ export function createTableFrameFromSearch(data: TraceSearchMetadata[], instance ], }, }, - { name: 'traceService', type: FieldType.string, config: { displayNameFromDS: 'Trace service' } }, - { name: 'traceName', type: FieldType.string, config: { displayNameFromDS: 'Trace name' } }, - { name: 'startTime', type: FieldType.time, config: { displayNameFromDS: 'Start time' } }, - { name: 'traceDuration', type: FieldType.number, config: { displayNameFromDS: 'Duration', unit: 'ms' } }, + { name: 'traceService', type: FieldType.string, config: { displayNameFromDS: 'Trace service' }, values: [] }, + { name: 'traceName', type: FieldType.string, config: { displayNameFromDS: 'Trace name' }, values: [] }, + { name: 'startTime', type: FieldType.time, config: { displayNameFromDS: 'Start time' }, values: [] }, + { + name: 'traceDuration', + type: FieldType.number, + config: { displayNameFromDS: 'Duration', unit: 'ms' }, + values: [], + }, ], meta: { preferredVisualisationType: 'table', @@ -826,6 +837,7 @@ const traceSubFrame = ( name: attr.key, type: FieldType.string, config: { displayNameFromDS: attr.key }, + values: [], }; }); spanSet.spans.forEach((span) => { @@ -837,6 +849,7 @@ const traceSubFrame = ( name: attr.key, type: FieldType.string, config: { displayNameFromDS: attr.key }, + values: [], }; }); }); @@ -849,10 +862,12 @@ const traceSubFrame = ( config: { custom: { hidden: true }, }, + values: [], }, { name: 'spanID', type: FieldType.string, + values: [], config: { unit: 'string', displayNameFromDS: 'Span ID', @@ -893,12 +908,14 @@ const traceSubFrame = ( { name: 'name', type: FieldType.string, + values: [], config: { displayNameFromDS: 'Name', custom: { hidden: !hasNameAttribute } }, }, ...Object.values(spanDynamicAttrs).sort((a, b) => a.name.localeCompare(b.name)), { name: 'duration', type: FieldType.number, + values: [], config: { displayNameFromDS: 'Duration', unit: 'ns', diff --git a/public/app/plugins/datasource/tempo/variables.ts b/public/app/plugins/datasource/tempo/variables.ts index afcfe6d0487..29c76129ca7 100644 --- a/public/app/plugins/datasource/tempo/variables.ts +++ b/public/app/plugins/datasource/tempo/variables.ts @@ -1,7 +1,7 @@ import { from, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { DataQueryRequest, DataQueryResponse, CustomVariableSupport } from '@grafana/data'; +import { DataQueryRequest, CustomVariableSupport, MetricFindValue } from '@grafana/data'; import { TempoVariableQuery, TempoVariableQueryEditor } from './VariableQueryEditor'; import { TempoDatasource } from './datasource'; @@ -13,7 +13,7 @@ export class TempoVariableSupport extends CustomVariableSupport): Observable { + query(request: DataQueryRequest): Observable<{ data: MetricFindValue[] }> { if (!this.datasource) { throw new Error('Datasource not initialized'); } diff --git a/public/app/plugins/datasource/zipkin/utils/transforms.ts b/public/app/plugins/datasource/zipkin/utils/transforms.ts index 0ecc391a39f..e3c8c0a2cdf 100644 --- a/public/app/plugins/datasource/zipkin/utils/transforms.ts +++ b/public/app/plugins/datasource/zipkin/utils/transforms.ts @@ -9,16 +9,16 @@ export function transformResponse(zSpans: ZipkinSpan[]): DataFrame { const spanRows = zSpans.map(transformSpan); const frame = new MutableDataFrame({ fields: [ - { name: 'traceID', type: FieldType.string }, - { name: 'spanID', type: FieldType.string }, - { name: 'parentSpanID', type: FieldType.string }, - { name: 'operationName', type: FieldType.string }, - { name: 'serviceName', type: FieldType.string }, - { name: 'serviceTags', type: FieldType.other }, - { name: 'startTime', type: FieldType.number }, - { name: 'duration', type: FieldType.number }, - { name: 'logs', type: FieldType.other }, - { name: 'tags', type: FieldType.other }, + { name: 'traceID', type: FieldType.string, values: [] }, + { name: 'spanID', type: FieldType.string, values: [] }, + { name: 'parentSpanID', type: FieldType.string, values: [] }, + { name: 'operationName', type: FieldType.string, values: [] }, + { name: 'serviceName', type: FieldType.string, values: [] }, + { name: 'serviceTags', type: FieldType.other, values: [] }, + { name: 'startTime', type: FieldType.number, values: [] }, + { name: 'duration', type: FieldType.number, values: [] }, + { name: 'logs', type: FieldType.other, values: [] }, + { name: 'tags', type: FieldType.other, values: [] }, ], meta: { preferredVisualisationType: 'trace',