@grafana/data#DataQueryResponseData TypeScript Examples

The following examples show how to use @grafana/data#DataQueryResponseData. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: runRequest.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
/**
 * All panels will be passed tables that have our best guess at colum type set
 *
 * This is also used by PanelChrome for snapshot support
 */
export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataFrame[] {
  if (!isArray(results)) {
    return [];
  }

  const dataFrames: DataFrame[] = [];

  for (const result of results) {
    const dataFrame = guessFieldTypes(toDataFrame(result));

    // clear out any cached calcs
    for (const field of dataFrame.fields) {
      field.calcs = null;
    }

    dataFrames.push(dataFrame);
  }

  return dataFrames;
}
Example #2
Source File: PanelModel.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
snapshotData?: DataQueryResponseData[];
Example #3
Source File: app_insights_datasource.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
async query(options: DataQueryRequest<AzureMonitorQuery>): Promise<DataQueryResponseData[]> {
    const queries = _.filter(options.targets, item => {
      return item.hide !== true;
    }).map((target: AzureMonitorQuery) => {
      const item = target.appInsights;
      let query: any;
      if (item.rawQuery) {
        query = this.createRawQueryRequest(item, options, target);
      } else {
        query = this.createMetricsRequest(item, options, target);
      }
      query.refId = target.refId;
      query.intervalMs = options.intervalMs;
      query.datasourceId = this.id;
      query.queryType = 'Application Insights';
      return query;
    });

    if (!queries || queries.length === 0) {
      // @ts-ignore
      return;
    }

    const { data } = await getBackendSrv().datasourceRequest({
      url: '/api/tsdb/query',
      method: 'POST',
      data: {
        from: options.range.from.valueOf().toString(),
        to: options.range.to.valueOf().toString(),
        queries,
      },
    });

    const result: DataQueryResponseData[] = [];
    if (data.results) {
      Object.values(data.results).forEach((queryRes: any) => {
        if (queryRes.meta && queryRes.meta.columns) {
          const columnNames = queryRes.meta.columns as string[];
          this.logAnalyticsColumns[queryRes.refId] = _.map(columnNames, n => ({ text: n, value: n }));
        }

        if (!queryRes.series) {
          return;
        }

        queryRes.series.forEach((series: any) => {
          const timeSerie: TimeSeries = {
            target: series.name,
            datapoints: series.points,
            refId: queryRes.refId,
            meta: queryRes.meta,
          };
          result.push(toDataFrame(timeSerie));
        });
      });
      return result;
    }

    return Promise.resolve([]);
  }
Example #4
Source File: azure_monitor_datasource.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
async query(options: DataQueryRequest<AzureMonitorQuery>): Promise<DataQueryResponseData[]> {
    const queries = _.filter(options.targets, item => {
      return (
        item.hide !== true &&
        item.azureMonitor.resourceGroup &&
        item.azureMonitor.resourceGroup !== this.defaultDropdownValue &&
        item.azureMonitor.resourceName &&
        item.azureMonitor.resourceName !== this.defaultDropdownValue &&
        item.azureMonitor.metricDefinition &&
        item.azureMonitor.metricDefinition !== this.defaultDropdownValue &&
        item.azureMonitor.metricName &&
        item.azureMonitor.metricName !== this.defaultDropdownValue
      );
    }).map(target => {
      const item = target.azureMonitor;

      // fix for timeGrainUnit which is a deprecated/removed field name
      if (item.timeGrainUnit && item.timeGrain !== 'auto') {
        item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrain, item.timeGrainUnit);
      }

      const subscriptionId = this.templateSrv.replace(target.subscription || this.subscriptionId, options.scopedVars);
      const resourceGroup = this.templateSrv.replace(item.resourceGroup, options.scopedVars);
      const resourceName = this.templateSrv.replace(item.resourceName, options.scopedVars);
      const metricNamespace = this.templateSrv.replace(item.metricNamespace, options.scopedVars);
      const metricDefinition = this.templateSrv.replace(item.metricDefinition, options.scopedVars);
      const timeGrain = this.templateSrv.replace((item.timeGrain || '').toString(), options.scopedVars);
      const aggregation = this.templateSrv.replace(item.aggregation, options.scopedVars);
      const top = this.templateSrv.replace(item.top || '', options.scopedVars);

      return {
        refId: target.refId,
        intervalMs: options.intervalMs,
        datasourceId: this.id,
        subscription: subscriptionId,
        queryType: 'Azure Monitor',
        type: 'timeSeriesQuery',
        raw: false,
        azureMonitor: {
          resourceGroup: resourceGroup,
          resourceName: resourceName,
          metricDefinition: metricDefinition,
          timeGrain: timeGrain,
          allowedTimeGrainsMs: item.allowedTimeGrainsMs,
          metricName: this.templateSrv.replace(item.metricName, options.scopedVars),
          metricNamespace:
            metricNamespace && metricNamespace !== this.defaultDropdownValue ? metricNamespace : metricDefinition,
          aggregation: aggregation,
          dimension: this.templateSrv.replace(item.dimension, options.scopedVars),
          top: top || '10',
          dimensionFilter: this.templateSrv.replace(item.dimensionFilter, options.scopedVars),
          alias: item.alias,
          format: target.format,
        },
      };
    });

    if (!queries || queries.length === 0) {
      return Promise.resolve([]);
    }

    const { data } = await getBackendSrv().datasourceRequest({
      url: '/api/tsdb/query',
      method: 'POST',
      data: {
        from: options.range.from.valueOf().toString(),
        to: options.range.to.valueOf().toString(),
        queries,
      },
    });

    const result: DataQueryResponseData[] = [];
    if (data.results) {
      Object['values'](data.results).forEach((queryRes: any) => {
        if (!queryRes.series) {
          return;
        }
        queryRes.series.forEach((series: any) => {
          const timeSerie: TimeSeries = {
            target: series.name,
            datapoints: series.points,
            refId: queryRes.refId,
            meta: queryRes.meta,
          };
          result.push(toDataFrame(timeSerie));
        });
      });
      return result;
    }

    return Promise.resolve([]);
  }
Example #5
Source File: datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('PrometheusDatasource', () => {
  const instanceSettings = ({
    url: 'proxied',
    directUrl: 'direct',
    user: 'test',
    password: 'mupp',
    jsonData: { httpMethod: 'GET' },
  } as unknown) as DataSourceInstanceSettings<PromOptions>;

  let ds: PrometheusDatasource;
  beforeEach(() => {
    ds = new PrometheusDatasource(instanceSettings);
  });

  describe('When querying prometheus with one target using query editor target spec', () => {
    describe('and query syntax is valid', () => {
      let results: any;
      const query = {
        range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
        targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
        interval: '60s',
      };

      // Interval alignment with step
      const urlExpected = `proxied/api/v1/query_range?query=${encodeURIComponent(
        'test{job="testjob"}'
      )}&start=60&end=180&step=60`;

      beforeEach(async () => {
        const response = {
          data: {
            status: 'success',
            data: {
              resultType: 'matrix',
              result: [
                {
                  metric: { __name__: 'test', job: 'testjob' },
                  values: [[60, '3846']],
                },
              ],
            },
          },
        };
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
        ds.query(query as any).subscribe((data: any) => {
          results = data;
        });
      });

      it('should generate the correct query', () => {
        const res = datasourceRequestMock.mock.calls[0][0];
        expect(res.method).toBe('GET');
        expect(res.url).toBe(urlExpected);
      });

      it('should return series list', async () => {
        expect(results.data.length).toBe(1);
        expect(results.data[0].target).toBe('test{job="testjob"}');
      });
    });

    describe('and query syntax is invalid', () => {
      let results: string;
      const query = {
        range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
        targets: [{ expr: 'tes;;t{job="testjob"}', format: 'time_series' }],
        interval: '60s',
      };

      const errMessage = 'parse error at char 25: could not parse remaining input';
      const response = {
        data: {
          status: 'error',
          errorType: 'bad_data',
          error: errMessage,
        },
      };

      it('should generate an error', () => {
        datasourceRequestMock.mockImplementation(() => Promise.reject(response));
        ds.query(query as any).subscribe((e: any) => {
          results = e.message;
          expect(results).toBe(`"${errMessage}"`);
        });
      });
    });
  });

  describe('When querying prometheus with one target which returns multiple series', () => {
    let results: any;
    const start = 60;
    const end = 360;
    const step = 60;

    const query = {
      range: { from: time({ seconds: start }), to: time({ seconds: end }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
      interval: '60s',
    };

    beforeEach(async () => {
      const response = {
        status: 'success',
        data: {
          data: {
            resultType: 'matrix',
            result: [
              {
                metric: { __name__: 'test', job: 'testjob', series: 'series 1' },
                values: [
                  [start + step * 1, '3846'],
                  [start + step * 3, '3847'],
                  [end - step * 1, '3848'],
                ],
              },
              {
                metric: { __name__: 'test', job: 'testjob', series: 'series 2' },
                values: [[start + step * 2, '4846']],
              },
            ],
          },
        },
      };

      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));

      ds.query(query as any).subscribe((data: any) => {
        results = data;
      });
    });

    it('should be same length', () => {
      expect(results.data.length).toBe(2);
      expect(results.data[0].datapoints.length).toBe((end - start) / step + 1);
      expect(results.data[1].datapoints.length).toBe((end - start) / step + 1);
    });

    it('should fill null until first datapoint in response', () => {
      expect(results.data[0].datapoints[0][1]).toBe(start * 1000);
      expect(results.data[0].datapoints[0][0]).toBe(null);
      expect(results.data[0].datapoints[1][1]).toBe((start + step * 1) * 1000);
      expect(results.data[0].datapoints[1][0]).toBe(3846);
    });

    it('should fill null after last datapoint in response', () => {
      const length = (end - start) / step + 1;
      expect(results.data[0].datapoints[length - 2][1]).toBe((end - step * 1) * 1000);
      expect(results.data[0].datapoints[length - 2][0]).toBe(3848);
      expect(results.data[0].datapoints[length - 1][1]).toBe(end * 1000);
      expect(results.data[0].datapoints[length - 1][0]).toBe(null);
    });

    it('should fill null at gap between series', () => {
      expect(results.data[0].datapoints[2][1]).toBe((start + step * 2) * 1000);
      expect(results.data[0].datapoints[2][0]).toBe(null);
      expect(results.data[1].datapoints[1][1]).toBe((start + step * 1) * 1000);
      expect(results.data[1].datapoints[1][0]).toBe(null);
      expect(results.data[1].datapoints[3][1]).toBe((start + step * 3) * 1000);
      expect(results.data[1].datapoints[3][0]).toBe(null);
    });
  });

  describe('When querying prometheus with one target and instant = true', () => {
    let results: any;
    const urlExpected = `proxied/api/v1/query?query=${encodeURIComponent('test{job="testjob"}')}&time=123`;
    const query = {
      range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
      interval: '60s',
    };

    beforeEach(async () => {
      const response = {
        status: 'success',
        data: {
          data: {
            resultType: 'vector',
            result: [
              {
                metric: { __name__: 'test', job: 'testjob' },
                value: [123, '3846'],
              },
            ],
          },
        },
      };

      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any).subscribe((data: any) => {
        results = data;
      });
    });

    it('should generate the correct query', () => {
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should return series list', () => {
      expect(results.data.length).toBe(1);
      expect(results.data[0].target).toBe('test{job="testjob"}');
    });
  });

  describe('annotationQuery', () => {
    let results: any;
    const options: any = {
      annotation: {
        expr: 'ALERTS{alertstate="firing"}',
        tagKeys: 'job',
        titleFormat: '{{alertname}}',
        textFormat: '{{instance}}',
      },
      range: {
        from: time({ seconds: 63 }),
        to: time({ seconds: 123 }),
      },
    };

    const response = {
      status: 'success',
      data: {
        data: {
          resultType: 'matrix',
          result: [
            {
              metric: {
                __name__: 'ALERTS',
                alertname: 'InstanceDown',
                alertstate: 'firing',
                instance: 'testinstance',
                job: 'testjob',
              },
              values: [[123, '1']],
            },
          ],
        },
      },
    };

    describe('when time series query is cancelled', () => {
      it('should return empty results', async () => {
        datasourceRequestMock.mockImplementation(() => Promise.resolve({ cancelled: true }));

        await ds.annotationQuery(options).then((data: any) => {
          results = data;
        });

        expect(results).toEqual([]);
      });
    });

    describe('not use useValueForTime', () => {
      beforeEach(async () => {
        options.annotation.useValueForTime = false;
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));

        await ds.annotationQuery(options).then((data: any) => {
          results = data;
        });
      });

      it('should return annotation list', () => {
        expect(results.length).toBe(1);
        expect(results[0].tags).toContain('testjob');
        expect(results[0].title).toBe('InstanceDown');
        expect(results[0].text).toBe('testinstance');
        expect(results[0].time).toBe(123 * 1000);
      });
    });

    describe('use useValueForTime', () => {
      beforeEach(async () => {
        options.annotation.useValueForTime = true;
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));

        await ds.annotationQuery(options).then((data: any) => {
          results = data;
        });
      });

      it('should return annotation list', () => {
        expect(results[0].time).toEqual(1);
      });
    });

    describe('step parameter', () => {
      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      });

      it('should use default step for short range if no interval is given', () => {
        const query = {
          ...options,
          range: {
            from: time({ seconds: 63 }),
            to: time({ seconds: 123 }),
          },
        };
        ds.annotationQuery(query);
        const req = datasourceRequestMock.mock.calls[0][0];
        expect(req.url).toContain('step=60');
      });

      it('should use default step for short range when annotation step is empty string', () => {
        const query = {
          ...options,
          annotation: {
            ...options.annotation,
            step: '',
          },
          range: {
            from: time({ seconds: 63 }),
            to: time({ seconds: 123 }),
          },
        };
        ds.annotationQuery(query);
        const req = datasourceRequestMock.mock.calls[0][0];
        expect(req.url).toContain('step=60');
      });

      it('should use custom step for short range', () => {
        const annotation = {
          ...options.annotation,
          step: '10s',
        };
        const query = {
          ...options,
          annotation,
          range: {
            from: time({ seconds: 63 }),
            to: time({ seconds: 123 }),
          },
        };
        ds.annotationQuery(query);
        const req = datasourceRequestMock.mock.calls[0][0];
        expect(req.url).toContain('step=10');
      });

      it('should use custom step for short range', () => {
        const annotation = {
          ...options.annotation,
          step: '10s',
        };
        const query = {
          ...options,
          annotation,
          range: {
            from: time({ seconds: 63 }),
            to: time({ seconds: 123 }),
          },
        };
        ds.annotationQuery(query);
        const req = datasourceRequestMock.mock.calls[0][0];
        expect(req.url).toContain('step=10');
      });

      it('should use dynamic step on long ranges if no option was given', () => {
        const query = {
          ...options,
          range: {
            from: time({ seconds: 63 }),
            to: time({ hours: 24 * 30, seconds: 63 }),
          },
        };
        ds.annotationQuery(query);
        const req = datasourceRequestMock.mock.calls[0][0];
        // Range in seconds: (to - from) / 1000
        // Max_datapoints: 11000
        // Step: range / max_datapoints
        const step = 236;
        expect(req.url).toContain(`step=${step}`);
      });
    });

    describe('region annotations for sectors', () => {
      const options: any = {
        annotation: {
          expr: 'ALERTS{alertstate="firing"}',
          tagKeys: 'job',
          titleFormat: '{{alertname}}',
          textFormat: '{{instance}}',
        },
        range: {
          from: time({ seconds: 63 }),
          to: time({ seconds: 900 }),
        },
      };

      async function runAnnotationQuery(resultValues: Array<[number, string]>) {
        const response = {
          status: 'success',
          data: {
            data: {
              resultType: 'matrix',
              result: [
                {
                  metric: { __name__: 'test', job: 'testjob' },
                  values: resultValues,
                },
              ],
            },
          },
        };

        options.annotation.useValueForTime = false;
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));

        return ds.annotationQuery(options);
      }

      it('should handle gaps and inactive values', async () => {
        const results = await runAnnotationQuery([
          [2 * 60, '1'],
          [3 * 60, '1'],
          // gap
          [5 * 60, '1'],
          [6 * 60, '1'],
          [7 * 60, '1'],
          [8 * 60, '0'], // false --> create new block
          [9 * 60, '1'],
        ]);
        expect(results.map(result => [result.time, result.timeEnd])).toEqual([
          [120000, 180000],
          [300000, 420000],
          [540000, 540000],
        ]);
      });

      it('should handle single region', async () => {
        const results = await runAnnotationQuery([
          [2 * 60, '1'],
          [3 * 60, '1'],
        ]);
        expect(results.map(result => [result.time, result.timeEnd])).toEqual([[120000, 180000]]);
      });

      it('should handle 0 active regions', async () => {
        const results = await runAnnotationQuery([
          [2 * 60, '0'],
          [3 * 60, '0'],
          [5 * 60, '0'],
        ]);
        expect(results.length).toBe(0);
      });

      it('should handle single active value', async () => {
        const results = await runAnnotationQuery([[2 * 60, '1']]);
        expect(results.map(result => [result.time, result.timeEnd])).toEqual([[120000, 120000]]);
      });
    });
  });

  describe('createAnnotationQueryOptions', () => {
    it.each`
      options                                | expected
      ${{}}                                  | ${{ interval: '60s' }}
      ${{ annotation: {} }}                  | ${{ annotation: {}, interval: '60s' }}
      ${{ annotation: { step: undefined } }} | ${{ annotation: { step: undefined }, interval: '60s' }}
      ${{ annotation: { step: null } }}      | ${{ annotation: { step: null }, interval: '60s' }}
      ${{ annotation: { step: '' } }}        | ${{ annotation: { step: '' }, interval: '60s' }}
      ${{ annotation: { step: 0 } }}         | ${{ annotation: { step: 0 }, interval: '60s' }}
      ${{ annotation: { step: 5 } }}         | ${{ annotation: { step: 5 }, interval: '60s' }}
      ${{ annotation: { step: '5m' } }}      | ${{ annotation: { step: '5m' }, interval: '5m' }}
    `("when called with options: '$options'", ({ options, expected }) => {
      expect(ds.createAnnotationQueryOptions(options)).toEqual(expected);
    });
  });

  describe('When resultFormat is table and instant = true', () => {
    let results: any;
    const query = {
      range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
      interval: '60s',
    };

    beforeEach(async () => {
      const response = {
        status: 'success',
        data: {
          data: {
            resultType: 'vector',
            result: [
              {
                metric: { __name__: 'test', job: 'testjob' },
                value: [123, '3846'],
              },
            ],
          },
        },
      };

      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any).subscribe((data: any) => {
        results = data;
      });
    });

    it('should return result', () => {
      expect(results).not.toBe(null);
    });
  });

  describe('The "step" query parameter', () => {
    const response = {
      status: 'success',
      data: {
        data: {
          resultType: 'matrix',
          result: [] as DataQueryResponseData[],
        },
      },
    };

    it('should be min interval when greater than auto interval', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '10s',
          },
        ],
        interval: '5s',
      };
      const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';

      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('step should be fractional for sub second intervals', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [{ expr: 'test' }],
        interval: '100ms',
      };
      const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=0.1';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should be auto interval when greater than min interval', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '5s',
          },
        ],
        interval: '10s',
      };
      const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should result in querying fewer than 11000 data points', async () => {
      const query = {
        // 6 hour range
        range: { from: time({ hours: 1 }), to: time({ hours: 7 }) },
        targets: [{ expr: 'test' }],
        interval: '1s',
      };
      const end = 7 * 60 * 60;
      const start = 60 * 60;
      const urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should not apply min interval when interval * intervalFactor greater', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '10s',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
      };
      // times get rounded up to interval
      const urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=400&step=50';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should apply min interval when interval * intervalFactor smaller', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '15s',
            intervalFactor: 2,
          },
        ],
        interval: '5s',
      };
      const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should apply intervalFactor to auto interval when greater', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '5s',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
      };
      // times get aligned to interval
      const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=400&step=100';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should not not be affected by the 11000 data points limit when large enough', async () => {
      const query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'test',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
      };
      const end = 7 * 24 * 60 * 60;
      const start = 0;
      const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });

    it('should be determined by the 11000 data points limit when too small', async () => {
      const query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'test',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
      };
      let end = 7 * 24 * 60 * 60;
      end -= end % 55;
      const start = 0;
      const step = 55;
      const adjusted = alignRange(
        start,
        end,
        step,
        getTimeSrv()
          .timeRange()
          .to.utcOffset() * 60
      );
      const urlExpected =
        'proxied/api/v1/query_range?query=test' + '&start=' + adjusted.start + '&end=' + adjusted.end + '&step=' + step;
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);
    });
  });

  describe('The __interval and __interval_ms template variables', () => {
    const response = {
      status: 'success',
      data: {
        data: {
          resultType: 'matrix',
          result: [] as DataQueryResponseData[],
        },
      },
    };

    it('should be unchanged when auto interval is greater than min interval', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '5s',
          },
        ],
        interval: '10s',
        scopedVars: {
          __interval: { text: '10s', value: '10s' },
          __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
        },
      };

      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=60&end=420&step=10';

      templateSrv.replace = jest.fn(str => str);
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '10s',
          value: '10s',
        },
        __interval_ms: {
          text: 10000,
          value: 10000,
        },
      });
    });

    it('should be min interval when it is greater than auto interval', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '10s',
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=60&end=420&step=10';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      templateSrv.replace = jest.fn(str => str);
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '5s',
          value: '5s',
        },
        __interval_ms: {
          text: 5000,
          value: 5000,
        },
      });
    });

    it('should account for intervalFactor', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '5s',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
        scopedVars: {
          __interval: { text: '10s', value: '10s' },
          __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
        },
      };
      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=0&end=400&step=100';
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      templateSrv.replace = jest.fn(str => str);
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '10s',
          value: '10s',
        },
        __interval_ms: {
          text: 10000,
          value: 10000,
        },
      });

      expect(query.scopedVars.__interval.text).toBe('10s');
      expect(query.scopedVars.__interval.value).toBe('10s');
      expect(query.scopedVars.__interval_ms.text).toBe(10 * 1000);
      expect(query.scopedVars.__interval_ms.value).toBe(10 * 1000);
    });

    it('should be interval * intervalFactor when greater than min interval', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '10s',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=50&end=400&step=50';

      templateSrv.replace = jest.fn(str => str);
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '5s',
          value: '5s',
        },
        __interval_ms: {
          text: 5000,
          value: 5000,
        },
      });
    });

    it('should be min interval when greater than interval * intervalFactor', async () => {
      const query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '15s',
            intervalFactor: 2,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=60&end=420&step=15';

      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '5s',
          value: '5s',
        },
        __interval_ms: {
          text: 5000,
          value: 5000,
        },
      });
    });

    it('should be determined by the 11000 data points limit, accounting for intervalFactor', async () => {
      const query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      let end = 7 * 24 * 60 * 60;
      end -= end % 55;
      const start = 0;
      const step = 55;
      const adjusted = alignRange(
        start,
        end,
        step,
        getTimeSrv()
          .timeRange()
          .to.utcOffset() * 60
      );
      const urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[$__interval])') +
        '&start=' +
        adjusted.start +
        '&end=' +
        adjusted.end +
        '&step=' +
        step;
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      templateSrv.replace = jest.fn(str => str);
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.method).toBe('GET');
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[0][1]).toEqual({
        __interval: {
          text: '5s',
          value: '5s',
        },
        __interval_ms: {
          text: 5000,
          value: 5000,
        },
      });
    });
  });

  describe('The __range, __range_s and __range_ms variables', () => {
    const response = {
      status: 'success',
      data: {
        data: {
          resultType: 'matrix',
          result: [] as DataQueryResponseData[],
        },
      },
    };

    it('should use overridden ranges, not dashboard ranges', async () => {
      const expectedRangeSecond = 3600;
      const expectedRangeString = '3600s';
      const query = {
        range: {
          from: time({}),
          to: time({ hours: 1 }),
        },
        targets: [
          {
            expr: 'test[${__range_s}s]',
          },
        ],
        interval: '60s',
      };
      const urlExpected = `proxied/api/v1/query_range?query=${encodeURIComponent(
        query.targets[0].expr
      )}&start=0&end=3600&step=60`;

      templateSrv.replace = jest.fn(str => str);
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      ds.query(query as any);
      const res = datasourceRequestMock.mock.calls[0][0];
      expect(res.url).toBe(urlExpected);

      // @ts-ignore
      expect(templateSrv.replace.mock.calls[1][1]).toEqual({
        __range_s: {
          text: expectedRangeSecond,
          value: expectedRangeSecond,
        },
        __range: {
          text: expectedRangeString,
          value: expectedRangeString,
        },
        __range_ms: {
          text: expectedRangeSecond * 1000,
          value: expectedRangeSecond * 1000,
        },
      });
    });
  });
});
Example #6
Source File: result_transformer.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('Prometheus Result Transformer', () => {
  const ctx: any = {};

  beforeEach(() => {
    ctx.templateSrv = {
      replace: (str: string) => str,
    };
    ctx.resultTransformer = new ResultTransformer(ctx.templateSrv);
  });

  describe('When nothing is returned', () => {
    test('should return empty series', () => {
      const response = {
        status: 'success',
        data: {
          resultType: '',
          result: null as DataQueryResponseData[],
        },
      };
      const series = ctx.resultTransformer.transform({ data: response }, {});
      expect(series).toEqual([]);
    });
    test('should return empty table', () => {
      const response = {
        status: 'success',
        data: {
          resultType: '',
          result: null as DataQueryResponseData[],
        },
      };
      const table = ctx.resultTransformer.transform({ data: response }, { format: 'table' });
      expect(table).toMatchObject([{ type: 'table', rows: [] }]);
    });
  });

  describe('When resultFormat is table', () => {
    const response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob' },
            values: [[1443454528, '3846']],
          },
          {
            metric: {
              __name__: 'test',
              instance: 'localhost:8080',
              job: 'otherjob',
            },
            values: [[1443454529, '3847']],
          },
        ],
      },
    };

    it('should return table model', () => {
      const table = ctx.resultTransformer.transformMetricDataToTable(response.data.result, 0, 'A');
      expect(table.type).toBe('table');
      expect(table.rows).toEqual([
        [1443454528000, 'test', '', 'testjob', 3846],
        [1443454529000, 'test', 'localhost:8080', 'otherjob', 3847],
      ]);
      expect(table.columns).toMatchObject([
        { text: 'Time', type: 'time' },
        { text: '__name__', filterable: true },
        { text: 'instance', filterable: true },
        { text: 'job' },
        { text: 'Value' },
      ]);
      expect(table.columns[4].filterable).toBeUndefined();
      expect(table.refId).toBe('A');
    });

    it('should column title include refId if response count is more than 2', () => {
      const table = ctx.resultTransformer.transformMetricDataToTable(response.data.result, 2, 'B');
      expect(table.type).toBe('table');
      expect(table.columns).toMatchObject([
        { text: 'Time', type: 'time' },
        { text: '__name__' },
        { text: 'instance' },
        { text: 'job' },
        { text: 'Value #B' },
      ]);
    });
  });

  describe('When resultFormat is table and instant = true', () => {
    const response = {
      status: 'success',
      data: {
        resultType: 'vector',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob' },
            value: [1443454528, '3846'],
          },
        ],
      },
    };

    it('should return table model', () => {
      const table = ctx.resultTransformer.transformMetricDataToTable(response.data.result);
      expect(table.type).toBe('table');
      expect(table.rows).toEqual([[1443454528000, 'test', 'testjob', 3846]]);
      expect(table.columns).toMatchObject([
        { text: 'Time', type: 'time' },
        { text: '__name__' },
        { text: 'job' },
        { text: 'Value' },
      ]);
    });
  });

  describe('When resultFormat is heatmap', () => {
    const response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob', le: '1' },
            values: [
              [1445000010, '10'],
              [1445000020, '10'],
              [1445000030, '0'],
            ],
          },
          {
            metric: { __name__: 'test', job: 'testjob', le: '2' },
            values: [
              [1445000010, '20'],
              [1445000020, '10'],
              [1445000030, '30'],
            ],
          },
          {
            metric: { __name__: 'test', job: 'testjob', le: '3' },
            values: [
              [1445000010, '30'],
              [1445000020, '10'],
              [1445000030, '40'],
            ],
          },
        ],
      },
    };

    it('should convert cumulative histogram to regular', () => {
      const options = {
        format: 'heatmap',
        start: 1445000010,
        end: 1445000030,
        legendFormat: '{{le}}',
      };

      const result = ctx.resultTransformer.transform({ data: response }, options);
      expect(result).toEqual([
        {
          target: '1',
          query: undefined,
          datapoints: [
            [10, 1445000010000],
            [10, 1445000020000],
            [0, 1445000030000],
          ],
          tags: { __name__: 'test', job: 'testjob', le: '1' },
        },
        {
          target: '2',
          query: undefined,
          datapoints: [
            [10, 1445000010000],
            [0, 1445000020000],
            [30, 1445000030000],
          ],
          tags: { __name__: 'test', job: 'testjob', le: '2' },
        },
        {
          target: '3',
          query: undefined,
          datapoints: [
            [10, 1445000010000],
            [0, 1445000020000],
            [10, 1445000030000],
          ],
          tags: { __name__: 'test', job: 'testjob', le: '3' },
        },
      ]);
    });

    it('should handle missing datapoints', () => {
      const seriesList = [
        {
          datapoints: [
            [1, 1000],
            [2, 2000],
          ],
        },
        {
          datapoints: [
            [2, 1000],
            [5, 2000],
            [1, 3000],
          ],
        },
        {
          datapoints: [
            [3, 1000],
            [7, 2000],
          ],
        },
      ];
      const expected = [
        {
          datapoints: [
            [1, 1000],
            [2, 2000],
          ],
        },
        {
          datapoints: [
            [1, 1000],
            [3, 2000],
            [1, 3000],
          ],
        },
        {
          datapoints: [
            [1, 1000],
            [2, 2000],
          ],
        },
      ];
      const result = ctx.resultTransformer.transformToHistogramOverTime(seriesList);
      expect(result).toEqual(expected);
    });

    it('should throw error when data in wrong format', () => {
      const seriesList = [{ rows: [] as any[] }, { datapoints: [] as any[] }];
      expect(() => {
        ctx.resultTransformer.transformToHistogramOverTime(seriesList);
      }).toThrow();
    });

    it('should throw error when prometheus returned non-timeseries', () => {
      // should be { metric: {}, values: [] } for timeseries
      const metricData = { metric: {}, value: [] as any[] };
      expect(() => {
        ctx.resultTransformer.transformMetricData(metricData, { step: 1 }, 1000, 2000);
      }).toThrow();
    });
  });

  describe('When resultFormat is time series', () => {
    it('should transform matrix into timeseries', () => {
      const response = {
        status: 'success',
        data: {
          resultType: 'matrix',
          result: [
            {
              metric: { __name__: 'test', job: 'testjob' },
              values: [
                [0, '10'],
                [1, '10'],
                [2, '0'],
              ],
            },
          ],
        },
      };
      const options = {
        format: 'timeseries',
        start: 0,
        end: 2,
        refId: 'B',
      };

      const result = ctx.resultTransformer.transform({ data: response }, options);
      expect(result).toEqual([
        {
          target: 'test{job="testjob"}',
          query: undefined,
          datapoints: [
            [10, 0],
            [10, 1000],
            [0, 2000],
          ],
          tags: { job: 'testjob' },
          refId: 'B',
        },
      ]);
    });

    it('should fill timeseries with null values', () => {
      const response = {
        status: 'success',
        data: {
          resultType: 'matrix',
          result: [
            {
              metric: { __name__: 'test', job: 'testjob' },
              values: [
                [1, '10'],
                [2, '0'],
              ],
            },
          ],
        },
      };
      const options = {
        format: 'timeseries',
        step: 1,
        start: 0,
        end: 2,
      };

      const result = ctx.resultTransformer.transform({ data: response }, options);
      expect(result).toEqual([
        {
          target: 'test{job="testjob"}',
          query: undefined,
          datapoints: [
            [null, 0],
            [10, 1000],
            [0, 2000],
          ],
          tags: { job: 'testjob' },
        },
      ]);
    });

    it('should align null values with step', () => {
      const response = {
        status: 'success',
        data: {
          resultType: 'matrix',
          result: [
            {
              metric: { __name__: 'test', job: 'testjob' },
              values: [
                [4, '10'],
                [8, '10'],
              ],
            },
          ],
        },
      };
      const options = {
        format: 'timeseries',
        step: 2,
        start: 0,
        end: 8,
      };

      const result = ctx.resultTransformer.transform({ data: response }, options);
      expect(result).toEqual([
        {
          target: 'test{job="testjob"}',
          query: undefined,
          datapoints: [
            [null, 0],
            [null, 2000],
            [10, 4000],
            [null, 6000],
            [10, 8000],
          ],
          tags: { job: 'testjob' },
        },
      ]);
    });
  });
});