datasource.ts 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import { lastValueFrom, Observable, of } from 'rxjs';
  2. import { map } from 'rxjs/operators';
  3. import {
  4. DataQueryRequest,
  5. DataQueryResponse,
  6. DataSourceApi,
  7. DataSourceInstanceSettings,
  8. DataSourceJsonData,
  9. FieldType,
  10. MutableDataFrame,
  11. ScopedVars,
  12. } from '@grafana/data';
  13. import { BackendSrvRequest, FetchResponse, getBackendSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
  14. import { NodeGraphOptions } from 'app/core/components/NodeGraphSettings';
  15. import { serializeParams } from '../../../core/utils/fetch';
  16. import { apiPrefix } from './constants';
  17. import { ZipkinQuery, ZipkinSpan } from './types';
  18. import { createGraphFrames } from './utils/graphTransform';
  19. import { transformResponse } from './utils/transforms';
  20. export interface ZipkinJsonData extends DataSourceJsonData {
  21. nodeGraph?: NodeGraphOptions;
  22. }
  23. export class ZipkinDatasource extends DataSourceApi<ZipkinQuery, ZipkinJsonData> {
  24. uploadedJson: string | ArrayBuffer | null = null;
  25. nodeGraph?: NodeGraphOptions;
  26. constructor(
  27. private instanceSettings: DataSourceInstanceSettings<ZipkinJsonData>,
  28. private readonly templateSrv: TemplateSrv = getTemplateSrv()
  29. ) {
  30. super(instanceSettings);
  31. this.nodeGraph = instanceSettings.jsonData.nodeGraph;
  32. }
  33. query(options: DataQueryRequest<ZipkinQuery>): Observable<DataQueryResponse> {
  34. const target = options.targets[0];
  35. if (target.queryType === 'upload') {
  36. if (!this.uploadedJson) {
  37. return of({ data: [] });
  38. }
  39. try {
  40. const traceData = JSON.parse(this.uploadedJson as string);
  41. return of(responseToDataQueryResponse({ data: traceData }, this.nodeGraph?.enabled));
  42. } catch (error) {
  43. return of({ error: { message: 'JSON is not valid Zipkin format' }, data: [] });
  44. }
  45. }
  46. if (target.query) {
  47. const query = this.applyVariables(target, options.scopedVars);
  48. return this.request<ZipkinSpan[]>(`${apiPrefix}/trace/${encodeURIComponent(query.query)}`).pipe(
  49. map((res) => responseToDataQueryResponse(res, this.nodeGraph?.enabled))
  50. );
  51. }
  52. return of(emptyDataQueryResponse);
  53. }
  54. async metadataRequest(url: string, params?: Record<string, any>): Promise<any> {
  55. const res = await lastValueFrom(this.request(url, params, { hideFromInspector: true }));
  56. return res.data;
  57. }
  58. async testDatasource(): Promise<{ status: string; message: string }> {
  59. await this.metadataRequest(`${apiPrefix}/services`);
  60. return { status: 'success', message: 'Data source is working' };
  61. }
  62. getQueryDisplayText(query: ZipkinQuery): string {
  63. return query.query;
  64. }
  65. interpolateVariablesInQueries(queries: ZipkinQuery[], scopedVars: ScopedVars): ZipkinQuery[] {
  66. if (!queries || queries.length === 0) {
  67. return [];
  68. }
  69. return queries.map((query) => {
  70. return {
  71. ...query,
  72. datasource: this.getRef(),
  73. ...this.applyVariables(query, scopedVars),
  74. };
  75. });
  76. }
  77. applyVariables(query: ZipkinQuery, scopedVars: ScopedVars) {
  78. const expandedQuery = { ...query };
  79. return {
  80. ...expandedQuery,
  81. query: this.templateSrv.replace(query.query ?? '', scopedVars),
  82. };
  83. }
  84. private request<T = any>(
  85. apiUrl: string,
  86. data?: any,
  87. options?: Partial<BackendSrvRequest>
  88. ): Observable<FetchResponse<T>> {
  89. const params = data ? serializeParams(data) : '';
  90. const url = `${this.instanceSettings.url}${apiUrl}${params.length ? `?${params}` : ''}`;
  91. const req = {
  92. ...options,
  93. url,
  94. };
  95. return getBackendSrv().fetch<T>(req);
  96. }
  97. }
  98. function responseToDataQueryResponse(response: { data: ZipkinSpan[] }, nodeGraph = false): DataQueryResponse {
  99. let data = response?.data ? [transformResponse(response?.data)] : [];
  100. if (nodeGraph) {
  101. data.push(...createGraphFrames(response?.data));
  102. }
  103. return {
  104. data,
  105. };
  106. }
  107. const emptyDataQueryResponse = {
  108. data: [
  109. new MutableDataFrame({
  110. fields: [
  111. {
  112. name: 'trace',
  113. type: FieldType.trace,
  114. values: [],
  115. },
  116. ],
  117. meta: {
  118. preferredVisualisationType: 'trace',
  119. custom: {
  120. traceFormat: 'zipkin',
  121. },
  122. },
  123. }),
  124. ],
  125. };