@grafana/data#toUtc TypeScript Examples

The following examples show how to use @grafana/data#toUtc. 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: explore.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
parseRawTime = (value: any): TimeFragment => {
  if (value === null) {
    return null;
  }

  if (value.indexOf('now') !== -1) {
    return value;
  }
  if (value.length === 8) {
    return toUtc(value, 'YYYYMMDD');
  }
  if (value.length === 15) {
    return toUtc(value, 'YYYYMMDDTHHmmss');
  }
  // Backward compatibility
  if (value.length === 19) {
    return toUtc(value, 'YYYY-MM-DD HH:mm:ss');
  }

  if (!isNaN(value)) {
    const epoch = parseInt(value, 10);
    return toUtc(epoch);
  }

  return null;
}
Example #2
Source File: rendering.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
onMouseUp() {
    $(document).unbind('mouseup', this.mouseUpHandler.bind(this));
    this.mouseUpHandler = null;
    this.selection.active = false;

    const selectionRange = Math.abs(this.selection.x2 - this.selection.x1);
    if (this.selection.x2 >= 0 && selectionRange > MIN_SELECTION_WIDTH) {
      const timeFrom = this.xScale.invert(Math.min(this.selection.x1, this.selection.x2) - this.yAxisWidth);
      const timeTo = this.xScale.invert(Math.max(this.selection.x1, this.selection.x2) - this.yAxisWidth);

      this.ctrl.timeSrv.setTime({
        from: toUtc(timeFrom),
        to: toUtc(timeTo),
      });
    }

    this.clearSelection();
  }
Example #3
Source File: graph.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
onPlotSelected(event: JQueryEventObject, ranges: any) {
    if (this.panel.xaxis.mode !== 'time') {
      // Skip if panel in histogram or series mode
      this.plot.clearSelection();
      return;
    }

    if ((ranges.ctrlKey || ranges.metaKey) && (this.dashboard.meta.canEdit || this.dashboard.meta.canMakeEditable)) {
      // Add annotation
      setTimeout(() => {
        this.eventManager.updateTime(ranges.xaxis);
      }, 100);
    } else {
      this.scope.$apply(() => {
        this.timeSrv.setTime({
          from: toUtc(ranges.xaxis.from),
          to: toUtc(ranges.xaxis.to),
        });
      });
    }
  }
Example #4
Source File: index_pattern.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
describe('IndexPattern', () => {
  describe('when getting index for today', () => {
    test('should return correct index name', () => {
      const pattern = new IndexPattern('[asd-]YYYY.MM.DD', 'Daily');
      const expected = 'asd-' + toUtc().format('YYYY.MM.DD');

      expect(pattern.getIndexForToday()).toBe(expected);
    });
  });

  describe('when getting index list for time range', () => {
    describe('no interval', () => {
      test('should return correct index', () => {
        const pattern = new IndexPattern('my-metrics', null);
        const from = new Date(2015, 4, 30, 1, 2, 3);
        const to = new Date(2015, 5, 1, 12, 5, 6);
        expect(pattern.getIndexList(from, to)).toEqual('my-metrics');
      });
    });

    describe('daily', () => {
      test('should return correct index list', () => {
        const pattern = new IndexPattern('[asd-]YYYY.MM.DD', 'Daily');
        const from = new Date(1432940523000);
        const to = new Date(1433153106000);

        const expected = ['asd-2015.05.29', 'asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01'];

        expect(pattern.getIndexList(from, to)).toEqual(expected);
      });
    });
  });
});
Example #5
Source File: TimeSrv.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
shiftTime(direction: number) {
    const range = this.timeRange();
    const { from, to } = getShiftedTimeRange(direction, range);

    this.setTime({
      from: toUtc(from),
      to: toUtc(to),
    });
  }
Example #6
Source File: TimeSrv.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
private getTimeWindow(time: string, timeWindow: string) {
    const valueTime = parseInt(time, 10);
    let timeWindowMs;

    if (timeWindow.match(/^\d+$/) && parseInt(timeWindow, 10)) {
      // when time window specified in ms
      timeWindowMs = parseInt(timeWindow, 10);
    } else {
      timeWindowMs = kbn.interval_to_ms(timeWindow);
    }

    return {
      from: toUtc(valueTime - timeWindowMs / 2),
      to: toUtc(valueTime + timeWindowMs / 2),
    };
  }
Example #7
Source File: TimeSrv.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
private parseUrlParam(value: any) {
    if (value.indexOf('now') !== -1) {
      return value;
    }
    if (value.length === 8) {
      const utcValue = toUtc(value, 'YYYYMMDD');
      if (utcValue.isValid()) {
        return utcValue;
      }
    } else if (value.length === 15) {
      const utcValue = toUtc(value, 'YYYYMMDDTHHmmss');
      if (utcValue.isValid()) {
        return utcValue;
      }
    }

    if (!isNaN(value)) {
      const epoch = parseInt(value, 10);
      return toUtc(epoch);
    }

    return null;
  }
Example #8
Source File: timePicker.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
setup = (options?: any) => {
  const defaultOptions = {
    range: {
      from: toUtc('2019-01-01 10:00:00'),
      to: toUtc('2019-01-01 16:00:00'),
      raw: {
        from: 'now-6h',
        to: 'now',
      },
    },
    direction: 0,
  };

  return { ...defaultOptions, ...options };
}
Example #9
Source File: timePicker.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
getShiftedTimeRange = (direction: number, origRange: TimeRange): AbsoluteTimeRange => {
  const range = {
    from: toUtc(origRange.from),
    to: toUtc(origRange.to),
  };

  const timespan = (range.to.valueOf() - range.from.valueOf()) / 2;
  let to: number, from: number;

  if (direction === -1) {
    to = range.to.valueOf() - timespan;
    from = range.from.valueOf() - timespan;
  } else if (direction === 1) {
    to = range.to.valueOf() + timespan;
    from = range.from.valueOf() + timespan;
    if (to > Date.now() && range.to.valueOf() < Date.now()) {
      to = Date.now();
      from = range.from.valueOf();
    }
  } else {
    to = range.to.valueOf();
    from = range.from.valueOf();
  }

  return { from, to };
}
Example #10
Source File: timePicker.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
describe('getZoomedTimeRange', () => {
  describe('when called', () => {
    it('then it should return correct result', () => {
      const { range } = setup();
      const expectedRange: AbsoluteTimeRange = {
        from: toUtc('2019-01-01 07:00:00').valueOf(),
        to: toUtc('2019-01-01 19:00:00').valueOf(),
      };

      const result = getZoomedTimeRange(range, 2);

      expect(result).toEqual(expectedRange);
    });
  });
});
Example #11
Source File: HistoryListCtrl.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
formatBasicDate(date: DateTimeInput) {
    const now = this.dashboard.timezone === 'browser' ? dateTime() : toUtc();
    const then = this.dashboard.timezone === 'browser' ? dateTime(date) : toUtc(date);
    return then.from(now);
  }
Example #12
Source File: metric_find_query.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
raw = {
  from: toUtc('2018-04-25 10:00'),
  to: toUtc('2018-04-25 11:00'),
}
Example #13
Source File: PromExploreQueryEditor.test.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
setup = (renderMethod: any, propOverrides?: object) => {
  const datasourceMock: unknown = {};
  const datasource: PrometheusDatasource = datasourceMock as PrometheusDatasource;
  const onRunQuery = jest.fn();
  const onChange = jest.fn();
  const query: PromQuery = { expr: '', refId: 'A', interval: '1s' };
  const data: PanelData = {
    state: LoadingState.NotStarted,
    series: [],
    request: {
      requestId: '1',
      dashboardId: 1,
      interval: '1s',
      panelId: 1,
      range: {
        from: toUtc('2020-01-01', 'YYYY-MM-DD'),
        to: toUtc('2020-01-02', 'YYYY-MM-DD'),
        raw: {
          from: toUtc('2020-01-01', 'YYYY-MM-DD'),
          to: toUtc('2020-01-02', 'YYYY-MM-DD'),
        },
      },
      scopedVars: {},
      targets: [],
      timezone: 'GMT',
      app: 'Grafana',
      startTime: 0,
    },
    timeRange: {
      from: toUtc('2020-01-01', 'YYYY-MM-DD'),
      to: toUtc('2020-01-02', 'YYYY-MM-DD'),
      raw: {
        from: toUtc('2020-01-01', 'YYYY-MM-DD'),
        to: toUtc('2020-01-02', 'YYYY-MM-DD'),
      },
    },
  };
  const history: any[] = [];
  const exploreMode = 'Metrics';

  const props: any = {
    query,
    data,
    datasource,
    exploreMode,
    history,
    onChange,
    onRunQuery,
  };

  Object.assign(props, propOverrides);

  return renderMethod(<PromExploreQueryEditor {...props} />);
}
Example #14
Source File: LokiExploreQueryEditor.test.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
setup = (renderMethod: any, propOverrides?: object) => {
  const datasource: LokiDatasource = makeMockLokiDatasource({});
  datasource.languageProvider = new LokiLanguageProvider(datasource);
  const onRunQuery = jest.fn();
  const onChange = jest.fn();
  const query: LokiQuery = { expr: '', refId: 'A', maxLines: 0 };
  const data: PanelData = {
    state: LoadingState.NotStarted,
    series: [],
    request: {
      requestId: '1',
      dashboardId: 1,
      interval: '1s',
      panelId: 1,
      range: {
        from: toUtc('2020-01-01', 'YYYY-MM-DD'),
        to: toUtc('2020-01-02', 'YYYY-MM-DD'),
        raw: {
          from: toUtc('2020-01-01', 'YYYY-MM-DD'),
          to: toUtc('2020-01-02', 'YYYY-MM-DD'),
        },
      },
      scopedVars: {},
      targets: [],
      timezone: 'GMT',
      app: 'Grafana',
      startTime: 0,
    },
    timeRange: {
      from: toUtc('2020-01-01', 'YYYY-MM-DD'),
      to: toUtc('2020-01-02', 'YYYY-MM-DD'),
      raw: {
        from: toUtc('2020-01-01', 'YYYY-MM-DD'),
        to: toUtc('2020-01-02', 'YYYY-MM-DD'),
      },
    },
  };
  const history: any[] = [];
  const exploreMode: ExploreMode = ExploreMode.Logs;

  const props: any = {
    query,
    data,
    datasource,
    exploreMode,
    history,
    onChange,
    onRunQuery,
  };

  Object.assign(props, { ...props, ...propOverrides });
  return renderMethod(<LokiExploreQueryEditor {...props} />);
}
Example #15
Source File: timePicker.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
describe('getShiftedTimeRange', () => {
  describe('when called with a direction of -1', () => {
    it('then it should return correct result', () => {
      const { range, direction } = setup({ direction: -1 });
      const expectedRange: AbsoluteTimeRange = {
        from: toUtc('2019-01-01 07:00:00').valueOf(),
        to: toUtc('2019-01-01 13:00:00').valueOf(),
      };

      const result = getShiftedTimeRange(direction, range);

      expect(result).toEqual(expectedRange);
    });
  });

  describe('when called with a direction of 1', () => {
    it('then it should return correct result', () => {
      const { range, direction } = setup({ direction: 1 });
      const expectedRange: AbsoluteTimeRange = {
        from: toUtc('2019-01-01 13:00:00').valueOf(),
        to: toUtc('2019-01-01 19:00:00').valueOf(),
      };

      const result = getShiftedTimeRange(direction, range);

      expect(result).toEqual(expectedRange);
    });
  });

  describe('when called with any other direction', () => {
    it('then it should return correct result', () => {
      const { range, direction } = setup({ direction: 0 });
      const expectedRange: AbsoluteTimeRange = {
        from: toUtc('2019-01-01 10:00:00').valueOf(),
        to: toUtc('2019-01-01 16:00:00').valueOf(),
      };

      const result = getShiftedTimeRange(direction, range);

      expect(result).toEqual(expectedRange);
    });
  });
});
Example #16
Source File: index_pattern.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
getIndexForToday() {
    if (this.interval) {
      return toUtc().format(this.pattern);
    } else {
      return this.pattern;
    }
  }
Example #17
Source File: actions.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
t = toUtc()
Example #18
Source File: DashboardModel.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
getRelativeTime(date: DateTimeInput) {
    date = isDateTime(date) ? date : dateTime(date);

    return this.timezone === 'browser' ? dateTime(date).fromNow() : toUtc(date).fromNow();
  }
Example #19
Source File: DashboardModel.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
formatDate(date: DateTimeInput, format?: string) {
    date = isDateTime(date) ? date : dateTime(date);
    format = format || 'YYYY-MM-DD HH:mm:ss';
    const timezone = this.getTimezone();

    return timezone === 'browser' ? dateTime(date).format(format) : toUtc(date).format(format);
  }
Example #20
Source File: TimeSrv.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
zoomOut(factor: number) {
    const range = this.timeRange();
    const { from, to } = getZoomedTimeRange(range, factor);

    this.setTime({ from: toUtc(from), to: toUtc(to) });
  }
Example #21
Source File: PanelChrome.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
onChangeTimeRange = (timeRange: AbsoluteTimeRange) => {
    this.timeSrv.setTime({
      from: toUtc(timeRange.from),
      to: toUtc(timeRange.to),
    });
  };
Example #22
Source File: datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('ElasticDatasource', function(this: any) {
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  beforeEach(() => {
    jest.clearAllMocks();
  });

  const $rootScope = {
    $on: jest.fn(),
    appEvent: jest.fn(),
  };

  const templateSrv: any = {
    replace: jest.fn(text => {
      if (text.startsWith('$')) {
        return `resolvedVariable`;
      } else {
        return text;
      }
    }),
    getAdhocFilters: jest.fn(() => []),
  };

  const timeSrv: any = createTimeSrv('now-1h');

  const ctx = {
    $rootScope,
  } as any;

  function createTimeSrv(from: string) {
    const srv: any = {
      time: { from: from, to: 'now' },
    };

    srv.timeRange = jest.fn(() => {
      return {
        from: dateMath.parse(srv.time.from, false),
        to: dateMath.parse(srv.time.to, true),
      };
    });

    srv.setTime = jest.fn(time => {
      srv.time = time;
    });

    return srv;
  }

  function createDatasource(instanceSettings: DataSourceInstanceSettings<ElasticsearchOptions>) {
    createDatasourceWithTime(instanceSettings, timeSrv as TimeSrv);
  }

  function createDatasourceWithTime(
    instanceSettings: DataSourceInstanceSettings<ElasticsearchOptions>,
    timeSrv: TimeSrv
  ) {
    instanceSettings.jsonData = instanceSettings.jsonData || ({} as ElasticsearchOptions);
    ctx.ds = new ElasticDatasource(instanceSettings, templateSrv as TemplateSrv, timeSrv);
  }

  describe('When testing datasource with index pattern', () => {
    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: '[asd-]YYYY.MM.DD',
        jsonData: { interval: 'Daily', esVersion: 2 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);
    });

    it('should translate index pattern to current day', () => {
      let requestOptions: any;
      datasourceRequestMock.mockImplementation(options => {
        requestOptions = options;
        return Promise.resolve({ data: {} });
      });

      ctx.ds.testDatasource();

      const today = toUtc().format('YYYY.MM.DD');
      expect(requestOptions.url).toBe('http://es.com/asd-' + today + '/_mapping');
    });
  });

  describe('When issuing metric query with interval pattern', () => {
    let requestOptions: any, parts: any, header: any, query: any, result: any;

    beforeEach(async () => {
      createDatasource({
        url: 'http://es.com',
        database: '[asd-]YYYY.MM.DD',
        jsonData: { interval: 'Daily', esVersion: 2 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        requestOptions = options;
        return Promise.resolve({
          data: {
            responses: [
              {
                aggregations: {
                  '1': {
                    buckets: [
                      {
                        doc_count: 10,
                        key: 1000,
                      },
                    ],
                  },
                },
              },
            ],
          },
        });
      });

      query = {
        range: {
          from: toUtc([2015, 4, 30, 10]),
          to: toUtc([2015, 5, 1, 10]),
        },
        targets: [
          {
            alias: '$varAlias',
            bucketAggs: [{ type: 'date_histogram', field: '@timestamp', id: '1' }],
            metrics: [{ type: 'count', id: '1' }],
            query: 'escape\\:test',
          },
        ],
      };

      result = await ctx.ds.query(query);

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should translate index pattern to current day', () => {
      expect(header.index).toEqual(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']);
    });

    it('should not resolve the variable in the original alias field in the query', () => {
      expect(query.targets[0].alias).toEqual('$varAlias');
    });

    it('should resolve the alias variable for the alias/target in the result', () => {
      expect(result.data[0].target).toEqual('resolvedVariable');
    });

    it('should json escape lucene query', () => {
      const body = angular.fromJson(parts[1]);
      expect(body.query.bool.filter[1].query_string.query).toBe('escape\\:test');
    });
  });

  describe('When issuing logs query with interval pattern', () => {
    async function setupDataSource(jsonData?: Partial<ElasticsearchOptions>) {
      createDatasource({
        url: 'http://es.com',
        database: 'mock-index',
        jsonData: {
          interval: 'Daily',
          esVersion: 2,
          timeField: '@timestamp',
          ...(jsonData || {}),
        } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        return Promise.resolve(logsResponse);
      });

      const query = {
        range: {
          from: toUtc([2015, 4, 30, 10]),
          to: toUtc([2019, 7, 1, 10]),
        },
        targets: [
          {
            alias: '$varAlias',
            refId: 'A',
            bucketAggs: [{ type: 'date_histogram', settings: { interval: 'auto' }, id: '2' }],
            metrics: [{ type: 'count', id: '1' }],
            query: 'escape\\:test',
            interval: '10s',
            isLogsQuery: true,
            timeField: '@timestamp',
          },
        ],
      };

      const queryBuilderSpy = jest.spyOn(ctx.ds.queryBuilder, 'getLogsQuery');
      const response = await ctx.ds.query(query);
      return { queryBuilderSpy, response };
    }

    it('should call getLogsQuery()', async () => {
      const { queryBuilderSpy } = await setupDataSource();
      expect(queryBuilderSpy).toHaveBeenCalled();
    });

    it('should enhance fields with links', async () => {
      const { response } = await setupDataSource({
        dataLinks: [
          {
            field: 'host',
            url: 'http://localhost:3000/${__value.raw}',
          },
        ],
      });
      // 1 for logs and 1 for counts.
      expect(response.data.length).toBe(2);
      const links = response.data[0].fields.find((field: Field) => field.name === 'host').config.links;
      expect(links.length).toBe(1);
      expect(links[0].url).toBe('http://localhost:3000/${__value.raw}');
    });
  });

  describe('When issuing document query', () => {
    let requestOptions: any, parts: any, header: any;

    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: 'test',
        jsonData: { esVersion: 2 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        requestOptions = options;
        return Promise.resolve({ data: { responses: [] } });
      });

      ctx.ds.query({
        range: {
          from: dateTime([2015, 4, 30, 10]),
          to: dateTime([2015, 5, 1, 10]),
        },
        targets: [
          {
            bucketAggs: [],
            metrics: [{ type: 'raw_document' }],
            query: 'test',
          },
        ],
      });

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should set search type to query_then_fetch', () => {
      expect(header.search_type).toEqual('query_then_fetch');
    });

    it('should set size', () => {
      const body = angular.fromJson(parts[1]);
      expect(body.size).toBe(500);
    });
  });

  describe('When getting fields', () => {
    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: 'metricbeat',
        jsonData: { esVersion: 50 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        return Promise.resolve({
          data: {
            metricbeat: {
              mappings: {
                metricsets: {
                  _all: {},
                  properties: {
                    '@timestamp': { type: 'date' },
                    beat: {
                      properties: {
                        name: {
                          fields: { raw: { type: 'keyword' } },
                          type: 'string',
                        },
                        hostname: { type: 'string' },
                      },
                    },
                    system: {
                      properties: {
                        cpu: {
                          properties: {
                            system: { type: 'float' },
                            user: { type: 'float' },
                          },
                        },
                        process: {
                          properties: {
                            cpu: {
                              properties: {
                                total: { type: 'float' },
                              },
                            },
                            name: { type: 'string' },
                          },
                        },
                      },
                    },
                  },
                },
              },
            },
          },
        });
      });
    });

    it('should return nested fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
      });
      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual([
        '@timestamp',
        'beat.name.raw',
        'beat.name',
        'beat.hostname',
        'system.cpu.system',
        'system.cpu.user',
        'system.process.cpu.total',
        'system.process.name',
      ]);
    });

    it('should return number fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'number',
      });

      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']);
    });

    it('should return date fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'date',
      });

      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual(['@timestamp']);
    });
  });

  describe('When getting field mappings on indices with gaps', () => {
    const twoWeekTimeSrv: any = createTimeSrv('now-2w');

    const basicResponse = {
      data: {
        metricbeat: {
          mappings: {
            metricsets: {
              _all: {},
              properties: {
                '@timestamp': { type: 'date' },
                beat: {
                  properties: {
                    hostname: { type: 'string' },
                  },
                },
              },
            },
          },
        },
      },
    };

    const alternateResponse = {
      data: {
        metricbeat: {
          mappings: {
            metricsets: {
              _all: {},
              properties: {
                '@timestamp': { type: 'date' },
              },
            },
          },
        },
      },
    };

    beforeEach(() => {
      createDatasourceWithTime(
        {
          url: 'http://es.com',
          database: '[asd-]YYYY.MM.DD',
          jsonData: { interval: 'Daily', esVersion: 50 } as ElasticsearchOptions,
        } as DataSourceInstanceSettings<ElasticsearchOptions>,
        twoWeekTimeSrv
      );
    });

    it('should return fields of the newest available index', async () => {
      const twoDaysBefore = toUtc()
        .subtract(2, 'day')
        .format('YYYY.MM.DD');

      const threeDaysBefore = toUtc()
        .subtract(3, 'day')
        .format('YYYY.MM.DD');

      datasourceRequestMock.mockImplementation(options => {
        if (options.url === `http://es.com/asd-${twoDaysBefore}/_mapping`) {
          return Promise.resolve(basicResponse);
        } else if (options.url === `http://es.com/asd-${threeDaysBefore}/_mapping`) {
          return Promise.resolve(alternateResponse);
        }
        return Promise.reject({ status: 404 });
      });

      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
      });
      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual(['@timestamp', 'beat.hostname']);
    });

    it('should not retry when ES is down', async () => {
      const twoDaysBefore = toUtc()
        .subtract(2, 'day')
        .format('YYYY.MM.DD');

      datasourceRequestMock.mockImplementation(options => {
        if (options.url === `http://es.com/asd-${twoDaysBefore}/_mapping`) {
          return Promise.resolve(basicResponse);
        }
        return Promise.reject({ status: 500 });
      });

      expect.assertions(2);
      try {
        await ctx.ds.getFields({
          find: 'fields',
          query: '*',
        });
      } catch (e) {
        expect(e).toStrictEqual({ status: 500 });
        expect(datasourceRequestMock).toBeCalledTimes(1);
      }
    });

    it('should not retry more than 7 indices', async () => {
      datasourceRequestMock.mockImplementation(() => {
        return Promise.reject({ status: 404 });
      });

      expect.assertions(2);
      try {
        await ctx.ds.getFields({
          find: 'fields',
          query: '*',
        });
      } catch (e) {
        expect(e).toStrictEqual({ status: 404 });
        expect(datasourceRequestMock).toBeCalledTimes(7);
      }
    });
  });

  describe('When getting fields from ES 7.0', () => {
    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: 'genuine.es7._mapping.response',
        jsonData: { esVersion: 70 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        return Promise.resolve({
          data: {
            'genuine.es7._mapping.response': {
              mappings: {
                properties: {
                  '@timestamp_millis': {
                    type: 'date',
                    format: 'epoch_millis',
                  },
                  classification_terms: {
                    type: 'keyword',
                  },
                  domains: {
                    type: 'keyword',
                  },
                  ip_address: {
                    type: 'ip',
                  },
                  justification_blob: {
                    properties: {
                      criterion: {
                        type: 'text',
                        fields: {
                          keyword: {
                            type: 'keyword',
                            ignore_above: 256,
                          },
                        },
                      },
                      overall_vote_score: {
                        type: 'float',
                      },
                      shallow: {
                        properties: {
                          jsi: {
                            properties: {
                              sdb: {
                                properties: {
                                  dsel2: {
                                    properties: {
                                      'bootlegged-gille': {
                                        properties: {
                                          botness: {
                                            type: 'float',
                                          },
                                          general_algorithm_score: {
                                            type: 'float',
                                          },
                                        },
                                      },
                                      'uncombed-boris': {
                                        properties: {
                                          botness: {
                                            type: 'float',
                                          },
                                          general_algorithm_score: {
                                            type: 'float',
                                          },
                                        },
                                      },
                                    },
                                  },
                                },
                              },
                            },
                          },
                        },
                      },
                    },
                  },
                  overall_vote_score: {
                    type: 'float',
                  },
                  ua_terms_long: {
                    type: 'keyword',
                  },
                  ua_terms_short: {
                    type: 'keyword',
                  },
                },
              },
            },
          },
        });
      });
    });

    it('should return nested fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
      });

      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual([
        '@timestamp_millis',
        'classification_terms',
        'domains',
        'ip_address',
        'justification_blob.criterion.keyword',
        'justification_blob.criterion',
        'justification_blob.overall_vote_score',
        'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness',
        'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.general_algorithm_score',
        'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.botness',
        'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.general_algorithm_score',
        'overall_vote_score',
        'ua_terms_long',
        'ua_terms_short',
      ]);
    });

    it('should return number fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'number',
      });

      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual([
        'justification_blob.overall_vote_score',
        'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.botness',
        'justification_blob.shallow.jsi.sdb.dsel2.bootlegged-gille.general_algorithm_score',
        'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.botness',
        'justification_blob.shallow.jsi.sdb.dsel2.uncombed-boris.general_algorithm_score',
        'overall_vote_score',
      ]);
    });

    it('should return date fields', async () => {
      const fieldObjects = await ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'date',
      });

      const fields = _.map(fieldObjects, 'text');
      expect(fields).toEqual(['@timestamp_millis']);
    });
  });

  describe('When issuing aggregation query on es5.x', () => {
    let requestOptions: any, parts: any, header: any;

    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: 'test',
        jsonData: { esVersion: 5 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        requestOptions = options;
        return Promise.resolve({ data: { responses: [] } });
      });

      ctx.ds.query({
        range: {
          from: dateTime([2015, 4, 30, 10]),
          to: dateTime([2015, 5, 1, 10]),
        },
        targets: [
          {
            bucketAggs: [{ type: 'date_histogram', field: '@timestamp', id: '2' }],
            metrics: [{ type: 'count' }],
            query: 'test',
          },
        ],
      });

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should not set search type to count', () => {
      expect(header.search_type).not.toEqual('count');
    });

    it('should set size to 0', () => {
      const body = angular.fromJson(parts[1]);
      expect(body.size).toBe(0);
    });
  });

  describe('When issuing metricFind query on es5.x', () => {
    let requestOptions: any, parts, header: any, body: any, results: any;

    beforeEach(() => {
      createDatasource({
        url: 'http://es.com',
        database: 'test',
        jsonData: { esVersion: 5 } as ElasticsearchOptions,
      } as DataSourceInstanceSettings<ElasticsearchOptions>);

      datasourceRequestMock.mockImplementation(options => {
        requestOptions = options;
        return Promise.resolve({
          data: {
            responses: [
              {
                aggregations: {
                  '1': {
                    buckets: [
                      { doc_count: 1, key: 'test' },
                      {
                        doc_count: 2,
                        key: 'test2',
                        key_as_string: 'test2_as_string',
                      },
                    ],
                  },
                },
              },
            ],
          },
        });
      });

      ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}').then((res: any) => {
        results = res;
      });

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
      body = angular.fromJson(parts[1]);
    });

    it('should get results', () => {
      expect(results.length).toEqual(2);
    });

    it('should use key or key_as_string', () => {
      expect(results[0].text).toEqual('test');
      expect(results[1].text).toEqual('test2_as_string');
    });

    it('should not set search type to count', () => {
      expect(header.search_type).not.toEqual('count');
    });

    it('should set size to 0', () => {
      expect(body.size).toBe(0);
    });

    it('should not set terms aggregation size to 0', () => {
      expect(body['aggs']['1']['terms'].size).not.toBe(0);
    });
  });

  describe('query', () => {
    it('should replace range as integer not string', () => {
      const dataSource = new ElasticDatasource(
        {
          url: 'http://es.com',
          database: '[asd-]YYYY.MM.DD',
          jsonData: {
            interval: 'Daily',
            esVersion: 2,
            timeField: '@time',
          },
        } as DataSourceInstanceSettings<ElasticsearchOptions>,
        templateSrv as TemplateSrv,
        timeSrv as TimeSrv
      );
      (dataSource as any).post = jest.fn(() => Promise.resolve({ responses: [] }));
      dataSource.query(createElasticQuery());

      const query = ((dataSource as any).post as jest.Mock).mock.calls[0][1];
      expect(typeof JSON.parse(query.split('\n')[1]).query.bool.filter[0].range['@time'].gte).toBe('number');
    });
  });
});
Example #23
Source File: datasource.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
annotationQuery(options: any): Promise<any> {
    const annotation = options.annotation;
    const timeField = annotation.timeField || '@timestamp';
    const timeEndField = annotation.timeEndField || null;
    const queryString = annotation.query || '*';
    const tagsField = annotation.tagsField || 'tags';
    const textField = annotation.textField || null;

    const dateRanges = [];
    const rangeStart: any = {};
    rangeStart[timeField] = {
      from: options.range.from.valueOf(),
      to: options.range.to.valueOf(),
      format: 'epoch_millis',
    };
    dateRanges.push({ range: rangeStart });

    if (timeEndField) {
      const rangeEnd: any = {};
      rangeEnd[timeEndField] = {
        from: options.range.from.valueOf(),
        to: options.range.to.valueOf(),
        format: 'epoch_millis',
      };
      dateRanges.push({ range: rangeEnd });
    }

    const queryInterpolated = this.templateSrv.replace(queryString, {}, 'lucene');
    const query = {
      bool: {
        filter: [
          {
            bool: {
              should: dateRanges,
              minimum_should_match: 1,
            },
          },
          {
            query_string: {
              query: queryInterpolated,
            },
          },
        ],
      },
    };

    const data: any = {
      query,
      size: 10000,
    };

    // fields field not supported on ES 5.x
    if (this.esVersion < 5) {
      data['fields'] = [timeField, '_source'];
    }

    const header: any = {
      search_type: 'query_then_fetch',
      ignore_unavailable: true,
    };

    // old elastic annotations had index specified on them
    if (annotation.index) {
      header.index = annotation.index;
    } else {
      header.index = this.indexPattern.getIndexList(options.range.from, options.range.to);
    }

    const payload = angular.toJson(header) + '\n' + angular.toJson(data) + '\n';

    return this.post('_msearch', payload).then((res: any) => {
      const list = [];
      const hits = res.responses[0].hits.hits;

      const getFieldFromSource = (source: any, fieldName: any) => {
        if (!fieldName) {
          return;
        }

        const fieldNames = fieldName.split('.');
        let fieldValue = source;

        for (let i = 0; i < fieldNames.length; i++) {
          fieldValue = fieldValue[fieldNames[i]];
          if (!fieldValue) {
            console.log('could not find field in annotation: ', fieldName);
            return '';
          }
        }

        return fieldValue;
      };

      for (let i = 0; i < hits.length; i++) {
        const source = hits[i]._source;
        let time = getFieldFromSource(source, timeField);
        if (typeof hits[i].fields !== 'undefined') {
          const fields = hits[i].fields;
          if (_.isString(fields[timeField]) || _.isNumber(fields[timeField])) {
            time = fields[timeField];
          }
        }

        const event: {
          annotation: any;
          time: number;
          timeEnd?: number;
          text: string;
          tags: string | string[];
        } = {
          annotation: annotation,
          time: toUtc(time).valueOf(),
          text: getFieldFromSource(source, textField),
          tags: getFieldFromSource(source, tagsField),
        };

        if (timeEndField) {
          const timeEnd = getFieldFromSource(source, timeEndField);
          if (timeEnd) {
            event.timeEnd = toUtc(timeEnd).valueOf();
          }
        }

        // legacy support for title tield
        if (annotation.titleField) {
          const title = getFieldFromSource(source, annotation.titleField);
          if (title) {
            event.text = title + '\n' + event.text;
          }
        }

        if (typeof event.tags === 'string') {
          event.tags = event.tags.split(',');
        }

        list.push(event);
      }
      return list;
    });
  }
Example #24
Source File: Explore.test.tsx    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
setup = (renderMethod: any, propOverrides?: object) => {
  const props: ExploreProps = {
    changeSize: jest.fn(),
    datasourceInstance: {
      meta: {
        metrics: true,
        logs: true,
      },
      components: {
        ExploreStartPage: {},
      },
    } as DataSourceApi,
    datasourceMissing: false,
    exploreId: ExploreId.left,
    initializeExplore: jest.fn(),
    initialized: true,
    modifyQueries: jest.fn(),
    update: {
      datasource: false,
      queries: false,
      range: false,
      mode: false,
      ui: false,
    },
    refreshExplore: jest.fn(),
    scanning: false,
    scanRange: {
      from: '0',
      to: '0',
    },
    scanStart: jest.fn(),
    scanStopAction: scanStopAction,
    setQueries: jest.fn(),
    split: false,
    queryKeys: [],
    initialDatasource: 'test',
    initialQueries: [],
    initialRange: {
      from: toUtc('2019-01-01 10:00:00'),
      to: toUtc('2019-01-01 16:00:00'),
      raw: {
        from: 'now-6h',
        to: 'now',
      },
    },
    mode: ExploreMode.Metrics,
    initialUI: {
      showingTable: false,
      showingGraph: false,
      showingLogs: false,
    },
    isLive: false,
    syncedTimes: false,
    updateTimeRange: jest.fn(),
    graphResult: [],
    loading: false,
    absoluteRange: {
      from: 0,
      to: 0,
    },
    showingGraph: false,
    showingTable: false,
    timeZone: 'UTC',
    onHiddenSeriesChanged: jest.fn(),
    toggleGraph: toggleGraph,
    queryResponse: {
      state: LoadingState.NotStarted,
      series: [],
      request: ({
        requestId: '1',
        dashboardId: 0,
        interval: '1s',
        panelId: 1,
        scopedVars: {
          apps: {
            value: 'value',
          },
        },
        targets: [
          {
            refId: 'A',
          },
        ],
        timezone: 'UTC',
        app: CoreApp.Explore,
        startTime: 0,
      } as unknown) as DataQueryRequest,
      error: {} as DataQueryError,
      timeRange: {
        from: toUtc('2019-01-01 10:00:00'),
        to: toUtc('2019-01-01 16:00:00'),
        raw: {
          from: 'now-6h',
          to: 'now',
        },
      },
    },
    originPanelId: 1,
    addQueryRow: jest.fn(),
  };

  const store = configureStore();

  Object.assign(props, propOverrides);
  return renderMethod(
    <Provider store={store}>
      <Explore {...props} />
    </Provider>
  );
}
Example #25
Source File: app_insights_datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('AppInsightsDatasource', () => {
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  const ctx: any = {
    templateSrv: new TemplateSrv(),
  };

  beforeEach(() => {
    jest.clearAllMocks();
    ctx.instanceSettings = {
      jsonData: { appInsightsAppId: '3ad4400f-ea7d-465d-a8fb-43fb20555d85' },
      url: 'http://appinsightsapi',
    };

    ctx.ds = new Datasource(ctx.instanceSettings, ctx.templateSrv);
  });

  describe('When performing testDatasource', () => {
    describe('and a list of metrics is returned', () => {
      const response = {
        metrics: {
          'requests/count': {
            displayName: 'Server requests',
            defaultAggregation: 'sum',
          },
          'requests/duration': {
            displayName: 'Server requests',
            defaultAggregation: 'sum',
          },
        },
        dimensions: {
          'request/source': {
            displayName: 'Request source',
          },
        },
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => {
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return success status', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('success');
        });
      });
    });

    describe('and a PathNotFoundError error is returned', () => {
      const error = {
        data: {
          error: {
            code: 'PathNotFoundError',
            message: `An error message.`,
          },
        },
        status: 404,
        statusText: 'Not Found',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => {
          return Promise.reject(error);
        });
      });

      it('should return error status and a detailed error message', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('error');
          expect(results.message).toEqual(
            '1. Application Insights: Not Found: Invalid Application Id for Application Insights service. '
          );
        });
      });
    });

    describe('and an error is returned', () => {
      const error = {
        data: {
          error: {
            code: 'SomeOtherError',
            message: `An error message.`,
          },
        },
        status: 500,
        statusText: 'Error',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => {
          return Promise.reject(error);
        });
      });

      it('should return error status and a detailed error message', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('error');
          expect(results.message).toEqual('1. Application Insights: Error: SomeOtherError. An error message. ');
        });
      });
    });
  });

  describe('When performing raw query', () => {
    const queryString =
      'metrics ' +
      '| where $__timeFilter(timestamp) ' +
      '| where name == "testMetrics" ' +
      '| summarize max=max(valueMax) by bin(timestamp, $__interval), partition';

    const options = {
      range: {
        from: toUtc('2017-08-22T20:00:00Z'),
        to: toUtc('2017-08-22T23:59:00Z'),
      },
      targets: [
        {
          apiVersion: '2016-09-01',
          refId: 'A',
          queryType: 'Application Insights',
          appInsights: {
            rawQuery: true,
            rawQueryString: queryString,
            timeColumn: 'timestamp',
            valueColumn: 'max',
            segmentColumn: undefined as string,
          },
        },
      ],
    };

    describe('with no grouping', () => {
      const response: any = {
        results: {
          A: {
            refId: 'A',
            meta: {},
            series: [
              {
                name: 'PrimaryResult',
                points: [[2.2075, 1558278660000]],
              },
            ],
            tables: null,
          },
        },
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: any) => {
          expect(options.url).toContain('/api/tsdb/query');
          expect(options.data.queries.length).toBe(1);
          expect(options.data.queries[0].refId).toBe('A');
          expect(options.data.queries[0].appInsights.rawQueryString).toEqual(queryString);
          expect(options.data.queries[0].appInsights.timeColumn).toEqual('timestamp');
          expect(options.data.queries[0].appInsights.valueColumn).toEqual('max');
          expect(options.data.queries[0].appInsights.segmentColumn).toBeUndefined();
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of datapoints', () => {
        return ctx.ds.query(options).then((results: any) => {
          expect(results.data.length).toBe(1);
          const data = results.data[0] as DataFrame;
          expect(data.name).toEqual('PrimaryResult');
          expect(data.fields[0].values.length).toEqual(1);
          expect(data.fields[1].values.get(0)).toEqual(1558278660000);
          expect(data.fields[0].values.get(0)).toEqual(2.2075);
        });
      });
    });

    describe('with grouping', () => {
      const response: any = {
        results: {
          A: {
            refId: 'A',
            meta: {},
            series: [
              {
                name: 'paritionA',
                points: [[2.2075, 1558278660000]],
              },
            ],
            tables: null,
          },
        },
      };

      beforeEach(() => {
        options.targets[0].appInsights.segmentColumn = 'partition';
        datasourceRequestMock.mockImplementation((options: any) => {
          expect(options.url).toContain('/api/tsdb/query');
          expect(options.data.queries.length).toBe(1);
          expect(options.data.queries[0].refId).toBe('A');
          expect(options.data.queries[0].appInsights.rawQueryString).toEqual(queryString);
          expect(options.data.queries[0].appInsights.timeColumn).toEqual('timestamp');
          expect(options.data.queries[0].appInsights.valueColumn).toEqual('max');
          expect(options.data.queries[0].appInsights.segmentColumn).toEqual('partition');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of datapoints', () => {
        return ctx.ds.query(options).then((results: any) => {
          expect(results.data.length).toBe(1);
          const data = results.data[0] as DataFrame;
          expect(data.name).toEqual('paritionA');
          expect(data.fields[0].values.length).toEqual(1);
          expect(data.fields[1].values.get(0)).toEqual(1558278660000);
          expect(data.fields[0].values.get(0)).toEqual(2.2075);
        });
      });
    });
  });

  describe('When performing metric query', () => {
    const options = {
      range: {
        from: toUtc('2017-08-22T20:00:00Z'),
        to: toUtc('2017-08-22T23:59:00Z'),
      },
      targets: [
        {
          apiVersion: '2016-09-01',
          refId: 'A',
          queryType: 'Application Insights',
          appInsights: {
            metricName: 'exceptions/server',
            dimension: '',
            timeGrain: 'none',
          },
        },
      ],
    };

    describe('and with a single value', () => {
      const response: any = {
        results: {
          A: {
            refId: 'A',
            meta: {},
            series: [
              {
                name: 'exceptions/server',
                points: [[2.2075, 1558278660000]],
              },
            ],
            tables: null,
          },
        },
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: any) => {
          expect(options.url).toContain('/api/tsdb/query');
          expect(options.data.queries.length).toBe(1);
          expect(options.data.queries[0].refId).toBe('A');
          expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
          expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a single datapoint', () => {
        return ctx.ds.query(options).then((results: any) => {
          expect(results.data.length).toBe(1);
          const data = results.data[0] as DataFrame;
          expect(data.name).toEqual('exceptions/server');
          expect(data.fields[1].values.get(0)).toEqual(1558278660000);
          expect(data.fields[0].values.get(0)).toEqual(2.2075);
        });
      });
    });

    describe('and with an interval group and without a segment group by', () => {
      const response: any = {
        results: {
          A: {
            refId: 'A',
            meta: {},
            series: [
              {
                name: 'exceptions/server',
                points: [
                  [3, 1504108800000],
                  [6, 1504112400000],
                ],
              },
            ],
            tables: null,
          },
        },
      };

      beforeEach(() => {
        options.targets[0].appInsights.timeGrain = 'PT30M';
        datasourceRequestMock.mockImplementation((options: any) => {
          expect(options.url).toContain('/api/tsdb/query');
          expect(options.data.queries[0].refId).toBe('A');
          expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
          expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
          expect(options.data.queries[0].appInsights.timeGrain).toBe('PT30M');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of datapoints', () => {
        return ctx.ds.query(options).then((results: any) => {
          expect(results.data.length).toBe(1);
          const data = results.data[0] as DataFrame;
          expect(data.name).toEqual('exceptions/server');
          expect(data.fields[0].values.length).toEqual(2);
          expect(data.fields[1].values.get(0)).toEqual(1504108800000);
          expect(data.fields[0].values.get(0)).toEqual(3);
          expect(data.fields[1].values.get(1)).toEqual(1504112400000);
          expect(data.fields[0].values.get(1)).toEqual(6);
        });
      });
    });

    describe('and with a group by', () => {
      const response: any = {
        results: {
          A: {
            refId: 'A',
            meta: {},
            series: [
              {
                name: 'exceptions/server{client/city="Miami"}',
                points: [
                  [10, 1504108800000],
                  [20, 1504112400000],
                ],
              },
              {
                name: 'exceptions/server{client/city="San Antonio"}',
                points: [
                  [1, 1504108800000],
                  [2, 1504112400000],
                ],
              },
            ],
            tables: null,
          },
        },
      };

      describe('and with no alias specified', () => {
        beforeEach(() => {
          options.targets[0].appInsights.dimension = 'client/city';

          datasourceRequestMock.mockImplementation((options: any) => {
            expect(options.url).toContain('/api/tsdb/query');
            expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
            expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
            expect(options.data.queries[0].appInsights.dimension).toBe('client/city');
            return Promise.resolve({ data: response, status: 200 });
          });
        });

        it('should return a list of datapoints', () => {
          return ctx.ds.query(options).then((results: any) => {
            expect(results.data.length).toBe(2);
            let data = results.data[0] as DataFrame;
            expect(data.name).toEqual('exceptions/server{client/city="Miami"}');
            expect(data.fields[0].values.length).toEqual(2);
            expect(data.fields[1].values.get(0)).toEqual(1504108800000);
            expect(data.fields[0].values.get(0)).toEqual(10);
            expect(data.fields[1].values.get(1)).toEqual(1504112400000);
            expect(data.fields[0].values.get(1)).toEqual(20);
            data = results.data[1] as DataFrame;
            expect(data.name).toEqual('exceptions/server{client/city="San Antonio"}');
            expect(data.fields[0].values.length).toEqual(2);
            expect(data.fields[1].values.get(0)).toEqual(1504108800000);
            expect(data.fields[0].values.get(0)).toEqual(1);
            expect(data.fields[1].values.get(1)).toEqual(1504112400000);
            expect(data.fields[0].values.get(1)).toEqual(2);
          });
        });
      });
    });
  });

  describe('When performing metricFindQuery', () => {
    describe('with a metric names query', () => {
      const response = {
        metrics: {
          'exceptions/server': {},
          'requests/count': {},
        },
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('/metrics/metadata');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of metric names', () => {
        return ctx.ds.metricFindQuery('appInsightsMetricNames()').then((results: any) => {
          expect(results.length).toBe(2);
          expect(results[0].text).toBe('exceptions/server');
          expect(results[0].value).toBe('exceptions/server');
          expect(results[1].text).toBe('requests/count');
          expect(results[1].value).toBe('requests/count');
        });
      });
    });

    describe('with metadata group by query', () => {
      const response = {
        metrics: {
          'exceptions/server': {
            supportedAggregations: ['sum'],
            supportedGroupBy: {
              all: ['client/os', 'client/city', 'client/browser'],
            },
            defaultAggregation: 'sum',
          },
          'requests/count': {
            supportedAggregations: ['avg', 'sum', 'total'],
            supportedGroupBy: {
              all: ['client/os', 'client/city', 'client/browser'],
            },
            defaultAggregation: 'avg',
          },
        },
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('/metrics/metadata');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of group bys', () => {
        return ctx.ds.metricFindQuery('appInsightsGroupBys(requests/count)').then((results: any) => {
          expect(results[0].text).toContain('client/os');
          expect(results[0].value).toContain('client/os');
          expect(results[1].text).toContain('client/city');
          expect(results[1].value).toContain('client/city');
          expect(results[2].text).toContain('client/browser');
          expect(results[2].value).toContain('client/browser');
        });
      });
    });
  });

  describe('When getting Metric Names', () => {
    const response = {
      metrics: {
        'exceptions/server': {},
        'requests/count': {},
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        expect(options.url).toContain('/metrics/metadata');
        return Promise.resolve({ data: response, status: 200 });
      });
    });

    it('should return a list of metric names', () => {
      return ctx.ds.getAppInsightsMetricNames().then((results: any) => {
        expect(results.length).toBe(2);
        expect(results[0].text).toBe('exceptions/server');
        expect(results[0].value).toBe('exceptions/server');
        expect(results[1].text).toBe('requests/count');
        expect(results[1].value).toBe('requests/count');
      });
    });
  });

  describe('When getting Metric Metadata', () => {
    const response = {
      metrics: {
        'exceptions/server': {
          supportedAggregations: ['sum'],
          supportedGroupBy: {
            all: ['client/os', 'client/city', 'client/browser'],
          },
          defaultAggregation: 'sum',
        },
        'requests/count': {
          supportedAggregations: ['avg', 'sum', 'total'],
          supportedGroupBy: {
            all: ['client/os', 'client/city', 'client/browser'],
          },
          defaultAggregation: 'avg',
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        expect(options.url).toContain('/metrics/metadata');
        return Promise.resolve({ data: response, status: 200 });
      });
    });

    it('should return a list of group bys', () => {
      return ctx.ds.getAppInsightsMetricMetadata('requests/count').then((results: any) => {
        expect(results.primaryAggType).toEqual('avg');
        expect(results.supportedAggTypes).toContain('avg');
        expect(results.supportedAggTypes).toContain('sum');
        expect(results.supportedAggTypes).toContain('total');
        expect(results.supportedGroupBy).toContain('client/os');
        expect(results.supportedGroupBy).toContain('client/city');
        expect(results.supportedGroupBy).toContain('client/browser');
      });
    });
  });
});
Example #26
Source File: azure_log_analytics_datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('AzureLogAnalyticsDatasource', () => {
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  beforeEach(() => {
    jest.clearAllMocks();
    datasourceRequestMock.mockImplementation(jest.fn());
  });

  const ctx: any = {
    templateSrv: new TemplateSrv(),
  };

  beforeEach(() => {
    ctx.instanceSettings = {
      jsonData: { logAnalyticsSubscriptionId: 'xxx' },
      url: 'http://azureloganalyticsapi',
    };

    ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings, ctx.templateSrv);
  });

  describe('When the config option "Same as Azure Monitor" has been chosen', () => {
    const tableResponseWithOneColumn = {
      tables: [
        {
          name: 'PrimaryResult',
          columns: [
            {
              name: 'Category',
              type: 'string',
            },
          ],
          rows: [['Administrative'], ['Policy']],
        },
      ],
    };

    const workspaceResponse = {
      value: [
        {
          name: 'aworkspace',
          properties: {
            source: 'Azure',
            customerId: 'abc1b44e-3e57-4410-b027-6cc0ae6dee67',
          },
        },
      ],
    };

    let workspacesUrl: string;
    let azureLogAnalyticsUrl: string;

    beforeEach(async () => {
      ctx.instanceSettings.jsonData.subscriptionId = 'xxx';
      ctx.instanceSettings.jsonData.tenantId = 'xxx';
      ctx.instanceSettings.jsonData.clientId = 'xxx';
      ctx.instanceSettings.jsonData.azureLogAnalyticsSameAs = true;
      ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings, ctx.templateSrv);

      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        if (options.url.indexOf('Microsoft.OperationalInsights/workspaces') > -1) {
          workspacesUrl = options.url;
          return Promise.resolve({ data: workspaceResponse, status: 200 });
        } else {
          azureLogAnalyticsUrl = options.url;
          return Promise.resolve({ data: tableResponseWithOneColumn, status: 200 });
        }
      });

      await ctx.ds.metricFindQuery('workspace("aworkspace").AzureActivity  | distinct Category');
    });

    it('should use the loganalyticsazure plugin route', () => {
      expect(workspacesUrl).toContain('azuremonitor');
      expect(azureLogAnalyticsUrl).toContain('loganalyticsazure');
    });
  });

  describe('When performing testDatasource', () => {
    describe('and an error is returned', () => {
      const error = {
        data: {
          error: {
            code: 'InvalidApiVersionParameter',
            message: `An error message.`,
          },
        },
        status: 400,
        statusText: 'Bad Request',
      };

      beforeEach(() => {
        ctx.instanceSettings.jsonData.logAnalyticsSubscriptionId = 'xxx';
        ctx.instanceSettings.jsonData.logAnalyticsTenantId = 'xxx';
        ctx.instanceSettings.jsonData.logAnalyticsClientId = 'xxx';
        datasourceRequestMock.mockImplementation(() => Promise.reject(error));
      });

      it('should return error status and a detailed error message', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('error');
          expect(results.message).toEqual(
            '1. Azure Log Analytics: Bad Request: InvalidApiVersionParameter. An error message. '
          );
        });
      });
    });
  });

  describe('When performing query', () => {
    const options = {
      range: {
        from: toUtc('2017-08-22T20:00:00Z'),
        to: toUtc('2017-08-22T23:59:00Z'),
      },
      rangeRaw: {
        from: 'now-4h',
        to: 'now',
      },
      targets: [
        {
          apiVersion: '2016-09-01',
          refId: 'A',
          queryType: 'Azure Log Analytics',
          azureLogAnalytics: {
            resultFormat: 'time_series',
            query:
              'AzureActivity | where TimeGenerated > ago(2h) ' +
              '| summarize count() by Category, bin(TimeGenerated, 5min) ' +
              '| project TimeGenerated, Category, count_  | order by TimeGenerated asc',
          },
        },
      ],
    };

    const response = {
      tables: [
        {
          name: 'PrimaryResult',
          columns: [
            {
              name: 'TimeGenerated',
              type: 'datetime',
            },
            {
              name: 'Category',
              type: 'string',
            },
            {
              name: 'count_',
              type: 'long',
            },
          ],
          rows: [
            ['2018-06-02T20:20:00Z', 'Administrative', 2],
            ['2018-06-02T20:25:00Z', 'Administrative', 22],
            ['2018-06-02T20:30:00Z', 'Policy', 20],
          ],
        },
      ],
    };

    describe('in time series format', () => {
      describe('and the data is valid (has time, metric and value columns)', () => {
        beforeEach(() => {
          datasourceRequestMock.mockImplementation((options: { url: string }) => {
            expect(options.url).toContain('query=AzureActivity');
            return Promise.resolve({ data: response, status: 200 });
          });
        });

        it('should return a list of datapoints', () => {
          return ctx.ds.query(options).then((results: any) => {
            expect(results.data.length).toBe(2);
            expect(results.data[0].datapoints.length).toBe(2);
            expect(results.data[0].target).toEqual('Administrative');
            expect(results.data[0].datapoints[0][1]).toEqual(1527970800000);
            expect(results.data[0].datapoints[0][0]).toEqual(2);
            expect(results.data[0].datapoints[1][1]).toEqual(1527971100000);
            expect(results.data[0].datapoints[1][0]).toEqual(22);
          });
        });
      });

      describe('and the data has no time column)', () => {
        beforeEach(() => {
          const invalidResponse = {
            tables: [
              {
                name: 'PrimaryResult',
                columns: [
                  {
                    name: 'Category',
                    type: 'string',
                  },
                  {
                    name: 'count_',
                    type: 'long',
                  },
                ],
                rows: [['Administrative', 2]],
              },
            ],
          };

          datasourceRequestMock.mockImplementation((options: { url: string }) => {
            expect(options.url).toContain('query=AzureActivity');
            return Promise.resolve({ data: invalidResponse, status: 200 });
          });
        });

        it('should throw an exception', () => {
          ctx.ds.query(options).catch((err: any) => {
            expect(err.message).toContain('The Time Series format requires a time column.');
          });
        });
      });
    });

    describe('in tableformat', () => {
      beforeEach(() => {
        options.targets[0].azureLogAnalytics.resultFormat = 'table';
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('query=AzureActivity');
          return Promise.resolve({ data: response, status: 200 });
        });
      });

      it('should return a list of columns and rows', () => {
        return ctx.ds.query(options).then((results: any) => {
          expect(results.data[0].type).toBe('table');
          expect(results.data[0].columns.length).toBe(3);
          expect(results.data[0].rows.length).toBe(3);
          expect(results.data[0].columns[0].text).toBe('TimeGenerated');
          expect(results.data[0].columns[0].type).toBe('datetime');
          expect(results.data[0].columns[1].text).toBe('Category');
          expect(results.data[0].columns[1].type).toBe('string');
          expect(results.data[0].columns[2].text).toBe('count_');
          expect(results.data[0].columns[2].type).toBe('long');
          expect(results.data[0].rows[0][0]).toEqual('2018-06-02T20:20:00Z');
          expect(results.data[0].rows[0][1]).toEqual('Administrative');
          expect(results.data[0].rows[0][2]).toEqual(2);
        });
      });
    });
  });

  describe('When performing getSchema', () => {
    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        expect(options.url).toContain('metadata');
        return Promise.resolve({ data: FakeSchemaData.getlogAnalyticsFakeMetadata(), status: 200 });
      });
    });

    it('should return a schema with a table and rows', () => {
      return ctx.ds.azureLogAnalyticsDatasource.getSchema('myWorkspace').then((result: KustoSchema) => {
        expect(Object.keys(result.Databases.Default.Tables).length).toBe(2);
        expect(result.Databases.Default.Tables.Alert.Name).toBe('Alert');
        expect(result.Databases.Default.Tables.AzureActivity.Name).toBe('AzureActivity');
        expect(result.Databases.Default.Tables.Alert.OrderedColumns.length).toBe(69);
        expect(result.Databases.Default.Tables.AzureActivity.OrderedColumns.length).toBe(21);
        expect(result.Databases.Default.Tables.Alert.OrderedColumns[0].Name).toBe('TimeGenerated');
        expect(result.Databases.Default.Tables.Alert.OrderedColumns[0].Type).toBe('datetime');

        expect(Object.keys(result.Databases.Default.Functions).length).toBe(1);
        expect(result.Databases.Default.Functions.Func1.Name).toBe('Func1');
      });
    });
  });

  describe('When performing metricFindQuery', () => {
    let queryResults: AzureLogsVariable[];

    const workspacesResponse = {
      value: [
        {
          name: 'workspace1',
          properties: {
            customerId: 'eeee4fde-1aaa-4d60-9974-eeee562ffaa1',
          },
        },
        {
          name: 'workspace2',
          properties: {
            customerId: 'eeee4fde-1aaa-4d60-9974-eeee562ffaa2',
          },
        },
      ],
    };

    describe('and is the workspaces() macro', () => {
      beforeEach(async () => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('xxx');
          return Promise.resolve({ data: workspacesResponse, status: 200 });
        });

        queryResults = await ctx.ds.metricFindQuery('workspaces()');
      });

      it('should return a list of workspaces', () => {
        expect(queryResults.length).toBe(2);
        expect(queryResults[0].text).toBe('workspace1');
        expect(queryResults[0].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa1');
        expect(queryResults[1].text).toBe('workspace2');
        expect(queryResults[1].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa2');
      });
    });

    describe('and is the workspaces() macro with the subscription parameter', () => {
      beforeEach(async () => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('11112222-eeee-4949-9b2d-9106972f9123');
          return Promise.resolve({ data: workspacesResponse, status: 200 });
        });

        queryResults = await ctx.ds.metricFindQuery('workspaces(11112222-eeee-4949-9b2d-9106972f9123)');
      });

      it('should return a list of workspaces', () => {
        expect(queryResults.length).toBe(2);
        expect(queryResults[0].text).toBe('workspace1');
        expect(queryResults[0].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa1');
        expect(queryResults[1].text).toBe('workspace2');
        expect(queryResults[1].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa2');
      });
    });

    describe('and is the workspaces() macro with the subscription parameter quoted', () => {
      beforeEach(async () => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('11112222-eeee-4949-9b2d-9106972f9123');
          return Promise.resolve({ data: workspacesResponse, status: 200 });
        });

        queryResults = await ctx.ds.metricFindQuery('workspaces("11112222-eeee-4949-9b2d-9106972f9123")');
      });

      it('should return a list of workspaces', () => {
        expect(queryResults.length).toBe(2);
        expect(queryResults[0].text).toBe('workspace1');
        expect(queryResults[0].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa1');
        expect(queryResults[1].text).toBe('workspace2');
        expect(queryResults[1].value).toBe('eeee4fde-1aaa-4d60-9974-eeee562ffaa2');
      });
    });

    describe('and is a custom query', () => {
      const tableResponseWithOneColumn = {
        tables: [
          {
            name: 'PrimaryResult',
            columns: [
              {
                name: 'Category',
                type: 'string',
              },
            ],
            rows: [['Administrative'], ['Policy']],
          },
        ],
      };

      const workspaceResponse = {
        value: [
          {
            name: 'aworkspace',
            properties: {
              source: 'Azure',
              customerId: 'abc1b44e-3e57-4410-b027-6cc0ae6dee67',
            },
          },
        ],
      };

      beforeEach(async () => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          if (options.url.indexOf('Microsoft.OperationalInsights/workspaces') > -1) {
            return Promise.resolve({ data: workspaceResponse, status: 200 });
          } else {
            return Promise.resolve({ data: tableResponseWithOneColumn, status: 200 });
          }
        });

        queryResults = await ctx.ds.metricFindQuery('workspace("aworkspace").AzureActivity  | distinct Category');
      });

      it('should return a list of categories in the correct format', () => {
        expect(queryResults.length).toBe(2);
        expect(queryResults[0].text).toBe('Administrative');
        expect(queryResults[0].value).toBe('Administrative');
        expect(queryResults[1].text).toBe('Policy');
        expect(queryResults[1].value).toBe('Policy');
      });
    });
  });

  describe('When performing annotationQuery', () => {
    const tableResponse = {
      tables: [
        {
          name: 'PrimaryResult',
          columns: [
            {
              name: 'TimeGenerated',
              type: 'datetime',
            },
            {
              name: 'Text',
              type: 'string',
            },
            {
              name: 'Tags',
              type: 'string',
            },
          ],
          rows: [
            ['2018-06-02T20:20:00Z', 'Computer1', 'tag1,tag2'],
            ['2018-06-02T20:28:00Z', 'Computer2', 'tag2'],
          ],
        },
      ],
    };

    const workspaceResponse = {
      value: [
        {
          name: 'aworkspace',
          properties: {
            source: 'Azure',
            customerId: 'abc1b44e-3e57-4410-b027-6cc0ae6dee67',
          },
        },
      ],
    };

    let annotationResults: any[];

    beforeEach(async () => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        if (options.url.indexOf('Microsoft.OperationalInsights/workspaces') > -1) {
          return Promise.resolve({ data: workspaceResponse, status: 200 });
        } else {
          return Promise.resolve({ data: tableResponse, status: 200 });
        }
      });

      annotationResults = await ctx.ds.annotationQuery({
        annotation: {
          rawQuery: 'Heartbeat | where $__timeFilter()| project TimeGenerated, Text=Computer, tags="test"',
          workspace: 'abc1b44e-3e57-4410-b027-6cc0ae6dee67',
        },
        range: {
          from: toUtc('2017-08-22T20:00:00Z'),
          to: toUtc('2017-08-22T23:59:00Z'),
        },
        rangeRaw: {
          from: 'now-4h',
          to: 'now',
        },
      });
    });

    it('should return a list of categories in the correct format', () => {
      expect(annotationResults.length).toBe(2);

      expect(annotationResults[0].time).toBe(1527970800000);
      expect(annotationResults[0].text).toBe('Computer1');
      expect(annotationResults[0].tags[0]).toBe('tag1');
      expect(annotationResults[0].tags[1]).toBe('tag2');

      expect(annotationResults[1].time).toBe(1527971280000);
      expect(annotationResults[1].text).toBe('Computer2');
      expect(annotationResults[1].tags[0]).toBe('tag2');
    });
  });
});
Example #27
Source File: azure_monitor_datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('AzureMonitorDatasource', () => {
  const ctx: any = {
    templateSrv: new TemplateSrv(),
  };
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  beforeEach(() => {
    jest.clearAllMocks();
    ctx.instanceSettings = {
      url: 'http://azuremonitor.com',
      jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f' },
      cloudName: 'azuremonitor',
    };

    ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings, ctx.templateSrv);
  });

  describe('When performing testDatasource', () => {
    describe('and an error is returned', () => {
      const error = {
        data: {
          error: {
            code: 'InvalidApiVersionParameter',
            message: `An error message.`,
          },
        },
        status: 400,
        statusText: 'Bad Request',
      };

      beforeEach(() => {
        ctx.instanceSettings.jsonData.tenantId = 'xxx';
        ctx.instanceSettings.jsonData.clientId = 'xxx';
        datasourceRequestMock.mockImplementation(() => Promise.reject(error));
      });

      it('should return error status and a detailed error message', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('error');
          expect(results.message).toEqual(
            '1. Azure Monitor: Bad Request: InvalidApiVersionParameter. An error message. '
          );
        });
      });
    });

    describe('and a list of resource groups is returned', () => {
      const response = {
        data: {
          value: [{ name: 'grp1' }, { name: 'grp2' }],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        ctx.instanceSettings.jsonData.tenantId = 'xxx';
        ctx.instanceSettings.jsonData.clientId = 'xxx';
        datasourceRequestMock.mockImplementation(() => Promise.resolve({ data: response, status: 200 }));
      });

      it('should return success status', () => {
        return ctx.ds.testDatasource().then((results: any) => {
          expect(results.status).toEqual('success');
        });
      });
    });
  });

  describe('When performing query', () => {
    const options = {
      range: {
        from: toUtc('2017-08-22T20:00:00Z'),
        to: toUtc('2017-08-22T23:59:00Z'),
      },
      targets: [
        {
          apiVersion: '2018-01-01',
          refId: 'A',
          queryType: 'Azure Monitor',
          azureMonitor: {
            resourceGroup: 'testRG',
            resourceName: 'testRN',
            metricDefinition: 'Microsoft.Compute/virtualMachines',
            metricNamespace: 'default',
            metricName: 'Percentage CPU',
            timeGrain: 'PT1H',
            alias: '{{metric}}',
          },
        },
      ],
    };

    const response: any = {
      results: {
        A: {
          refId: 'A',
          meta: {
            rawQuery:
              'aggregation=Average&api-version=2018-01-01&interval=PT1M' +
              '&metricnames=Percentage+CPU&timespan=2019-05-19T15%3A11%3A37Z%2F2019-05-19T21%3A11%3A37Z',
            unit: 'Percent',
          },
          series: [
            {
              name: 'Percentage CPU',
              points: [
                [2.2075, 1558278660000],
                [2.29, 1558278720000],
              ],
            },
          ],
          tables: null,
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        expect(options.url).toContain('/api/tsdb/query');
        return Promise.resolve({ data: response, status: 200 });
      });
    });

    it('should return a list of datapoints', () => {
      return ctx.ds.query(options).then((results: any) => {
        expect(results.data.length).toBe(1);
        const data = results.data[0] as DataFrame;
        expect(data.name).toEqual('Percentage CPU');
        expect(data.fields[1].values.get(0)).toEqual(1558278660000);
        expect(data.fields[0].values.get(0)).toEqual(2.2075);
        expect(data.fields[1].values.get(1)).toEqual(1558278720000);
        expect(data.fields[0].values.get(1)).toEqual(2.29);
      });
    });
  });

  describe('When performing metricFindQuery', () => {
    describe('with a subscriptions query', () => {
      const response = {
        data: {
          value: [
            { displayName: 'Primary', subscriptionId: 'sub1' },
            { displayName: 'Secondary', subscriptionId: 'sub2' },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      });

      it('should return a list of subscriptions', () => {
        return ctx.ds.metricFindQuery('subscriptions()').then((results: Array<{ text: string; value: string }>) => {
          expect(results.length).toBe(2);
          expect(results[0].text).toBe('Primary - sub1');
          expect(results[0].value).toBe('sub1');
          expect(results[1].text).toBe('Secondary - sub2');
          expect(results[1].value).toBe('sub2');
        });
      });
    });

    describe('with a resource groups query', () => {
      const response = {
        data: {
          value: [{ name: 'grp1' }, { name: 'grp2' }],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
      });

      it('should return a list of resource groups', () => {
        return ctx.ds.metricFindQuery('ResourceGroups()').then((results: Array<{ text: string; value: string }>) => {
          expect(results.length).toBe(2);
          expect(results[0].text).toBe('grp1');
          expect(results[0].value).toBe('grp1');
          expect(results[1].text).toBe('grp2');
          expect(results[1].value).toBe('grp2');
        });
      });
    });

    describe('with a resource groups query that specifies a subscription id', () => {
      const response = {
        data: {
          value: [{ name: 'grp1' }, { name: 'grp2' }],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          expect(options.url).toContain('11112222-eeee-4949-9b2d-9106972f9123');
          return Promise.resolve(response);
        });
      });

      it('should return a list of resource groups', () => {
        return ctx.ds
          .metricFindQuery('ResourceGroups(11112222-eeee-4949-9b2d-9106972f9123)')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toBe(2);
            expect(results[0].text).toBe('grp1');
            expect(results[0].value).toBe('grp1');
            expect(results[1].text).toBe('grp2');
            expect(results[1].value).toBe('grp2');
          });
      });
    });

    describe('with namespaces query', () => {
      const response = {
        data: {
          value: [
            {
              name: 'test',
              type: 'Microsoft.Network/networkInterfaces',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodesapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return a list of namespaces', () => {
        return ctx.ds
          .metricFindQuery('Namespaces(nodesapp)')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('Microsoft.Network/networkInterfaces');
            expect(results[0].value).toEqual('Microsoft.Network/networkInterfaces');
          });
      });
    });

    describe('with namespaces query that specifies a subscription id', () => {
      const response = {
        data: {
          value: [
            {
              name: 'test',
              type: 'Microsoft.Network/networkInterfaces',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/11112222-eeee-4949-9b2d-9106972f9123/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodesapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return a list of namespaces', () => {
        return ctx.ds
          .metricFindQuery('namespaces(11112222-eeee-4949-9b2d-9106972f9123, nodesapp)')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('Microsoft.Network/networkInterfaces');
            expect(results[0].value).toEqual('Microsoft.Network/networkInterfaces');
          });
      });
    });

    describe('with resource names query', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Failure Anomalies - nodeapp',
              type: 'microsoft.insights/alertrules',
            },
            {
              name: 'nodeapp',
              type: 'microsoft.insights/components',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodeapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return a list of resource names', () => {
        return ctx.ds
          .metricFindQuery('resourceNames(nodeapp, microsoft.insights/components )')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('nodeapp');
            expect(results[0].value).toEqual('nodeapp');
          });
      });
    });

    describe('with resource names query and that specifies a subscription id', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Failure Anomalies - nodeapp',
              type: 'microsoft.insights/alertrules',
            },
            {
              name: 'nodeapp',
              type: 'microsoft.insights/components',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/11112222-eeee-4949-9b2d-9106972f9123/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodeapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return a list of resource names', () => {
        return ctx.ds
          .metricFindQuery(
            'resourceNames(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, microsoft.insights/components )'
          )
          .then((results: any) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('nodeapp');
            expect(results[0].value).toEqual('nodeapp');
          });
      });
    });

    describe('with metric names query', () => {
      const response = {
        data: {
          value: [
            {
              name: {
                value: 'Percentage CPU',
                localizedValue: 'Percentage CPU',
              },
            },
            {
              name: {
                value: 'UsedCapacity',
                localizedValue: 'Used capacity',
              },
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(
            baseUrl +
              '/nodeapp/providers/microsoft.insights/components/rn/providers/microsoft.insights/' +
              'metricdefinitions?api-version=2018-01-01&metricnamespace=default'
          );
          return Promise.resolve(response);
        });
      });

      it('should return a list of metric names', () => {
        return ctx.ds
          .metricFindQuery('Metricnames(nodeapp, microsoft.insights/components, rn, default)')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(2);
            expect(results[0].text).toEqual('Percentage CPU');
            expect(results[0].value).toEqual('Percentage CPU');

            expect(results[1].text).toEqual('Used capacity');
            expect(results[1].value).toEqual('UsedCapacity');
          });
      });
    });

    describe('with metric names query and specifies a subscription id', () => {
      const response = {
        data: {
          value: [
            {
              name: {
                value: 'Percentage CPU',
                localizedValue: 'Percentage CPU',
              },
            },
            {
              name: {
                value: 'UsedCapacity',
                localizedValue: 'Used capacity',
              },
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/11112222-eeee-4949-9b2d-9106972f9123/resourceGroups';
          expect(options.url).toBe(
            baseUrl +
              '/nodeapp/providers/microsoft.insights/components/rn/providers/microsoft.insights/' +
              'metricdefinitions?api-version=2018-01-01&metricnamespace=default'
          );
          return Promise.resolve(response);
        });
      });

      it('should return a list of metric names', () => {
        return ctx.ds
          .metricFindQuery(
            'Metricnames(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, microsoft.insights/components, rn, default)'
          )
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(2);
            expect(results[0].text).toEqual('Percentage CPU');
            expect(results[0].value).toEqual('Percentage CPU');

            expect(results[1].text).toEqual('Used capacity');
            expect(results[1].value).toEqual('UsedCapacity');
          });
      });
    });

    describe('with metric namespace query', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Microsoft.Compute-virtualMachines',
              properties: {
                metricNamespaceName: 'Microsoft.Compute/virtualMachines',
              },
            },
            {
              name: 'Telegraf-mem',
              properties: {
                metricNamespaceName: 'Telegraf/mem',
              },
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(
            baseUrl +
              '/nodeapp/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metricNamespaces?api-version=2017-12-01-preview'
          );
          return Promise.resolve(response);
        });
      });

      it('should return a list of metric names', () => {
        return ctx.ds
          .metricFindQuery('Metricnamespace(nodeapp, Microsoft.Compute/virtualMachines, rn)')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(2);
            expect(results[0].text).toEqual('Microsoft.Compute-virtualMachines');
            expect(results[0].value).toEqual('Microsoft.Compute/virtualMachines');

            expect(results[1].text).toEqual('Telegraf-mem');
            expect(results[1].value).toEqual('Telegraf/mem');
          });
      });
    });

    describe('with metric namespace query and specifies a subscription id', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Microsoft.Compute-virtualMachines',
              properties: {
                metricNamespaceName: 'Microsoft.Compute/virtualMachines',
              },
            },
            {
              name: 'Telegraf-mem',
              properties: {
                metricNamespaceName: 'Telegraf/mem',
              },
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/11112222-eeee-4949-9b2d-9106972f9123/resourceGroups';
          expect(options.url).toBe(
            baseUrl +
              '/nodeapp/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metricNamespaces?api-version=2017-12-01-preview'
          );
          return Promise.resolve(response);
        });
      });

      it('should return a list of metric namespaces', () => {
        return ctx.ds
          .metricFindQuery(
            'Metricnamespace(11112222-eeee-4949-9b2d-9106972f9123, nodeapp, Microsoft.Compute/virtualMachines, rn)'
          )
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(2);
            expect(results[0].text).toEqual('Microsoft.Compute-virtualMachines');
            expect(results[0].value).toEqual('Microsoft.Compute/virtualMachines');

            expect(results[1].text).toEqual('Telegraf-mem');
            expect(results[1].value).toEqual('Telegraf/mem');
          });
      });
    });
  });

  describe('When performing getSubscriptions', () => {
    const response = {
      data: {
        value: [
          {
            id: '/subscriptions/99999999-cccc-bbbb-aaaa-9106972f9572',
            subscriptionId: '99999999-cccc-bbbb-aaaa-9106972f9572',
            tenantId: '99999999-aaaa-bbbb-cccc-51c4f982ec48',
            displayName: 'Primary Subscription',
            state: 'Enabled',
            subscriptionPolicies: {
              locationPlacementId: 'Public_2014-09-01',
              quotaId: 'PayAsYouGo_2014-09-01',
              spendingLimit: 'Off',
            },
            authorizationSource: 'RoleBased',
          },
        ],
        count: {
          type: 'Total',
          value: 1,
        },
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
    });

    it('should return list of Resource Groups', () => {
      return ctx.ds.getSubscriptions().then((results: Array<{ text: string; value: string }>) => {
        expect(results.length).toEqual(1);
        expect(results[0].text).toEqual('Primary Subscription - 99999999-cccc-bbbb-aaaa-9106972f9572');
        expect(results[0].value).toEqual('99999999-cccc-bbbb-aaaa-9106972f9572');
      });
    });
  });

  describe('When performing getResourceGroups', () => {
    const response = {
      data: {
        value: [{ name: 'grp1' }, { name: 'grp2' }],
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(() => Promise.resolve(response));
    });

    it('should return list of Resource Groups', () => {
      return ctx.ds.getResourceGroups().then((results: Array<{ text: string; value: string }>) => {
        expect(results.length).toEqual(2);
        expect(results[0].text).toEqual('grp1');
        expect(results[0].value).toEqual('grp1');
        expect(results[1].text).toEqual('grp2');
        expect(results[1].value).toEqual('grp2');
      });
    });
  });

  describe('When performing getMetricDefinitions', () => {
    const response = {
      data: {
        value: [
          {
            name: 'test',
            type: 'Microsoft.Network/networkInterfaces',
          },
          {
            location: 'northeurope',
            name: 'northeur',
            type: 'Microsoft.Compute/virtualMachines',
          },
          {
            location: 'westcentralus',
            name: 'us',
            type: 'Microsoft.Compute/virtualMachines',
          },
          {
            name: 'IHaveNoMetrics',
            type: 'IShouldBeFilteredOut',
          },
          {
            name: 'storageTest',
            type: 'Microsoft.Storage/storageAccounts',
          },
        ],
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        const baseUrl =
          'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
        expect(options.url).toBe(baseUrl + '/nodesapp/resources?api-version=2018-01-01');
        return Promise.resolve(response);
      });
    });

    it('should return list of Metric Definitions with no duplicates and no unsupported namespaces', () => {
      return ctx.ds
        .getMetricDefinitions('9935389e-9122-4ef9-95f9-1513dd24753f', 'nodesapp')
        .then((results: Array<{ text: string; value: string }>) => {
          expect(results.length).toEqual(7);
          expect(results[0].text).toEqual('Microsoft.Network/networkInterfaces');
          expect(results[0].value).toEqual('Microsoft.Network/networkInterfaces');
          expect(results[1].text).toEqual('Microsoft.Compute/virtualMachines');
          expect(results[1].value).toEqual('Microsoft.Compute/virtualMachines');
          expect(results[2].text).toEqual('Microsoft.Storage/storageAccounts');
          expect(results[2].value).toEqual('Microsoft.Storage/storageAccounts');
          expect(results[3].text).toEqual('Microsoft.Storage/storageAccounts/blobServices');
          expect(results[3].value).toEqual('Microsoft.Storage/storageAccounts/blobServices');
          expect(results[4].text).toEqual('Microsoft.Storage/storageAccounts/fileServices');
          expect(results[4].value).toEqual('Microsoft.Storage/storageAccounts/fileServices');
          expect(results[5].text).toEqual('Microsoft.Storage/storageAccounts/tableServices');
          expect(results[5].value).toEqual('Microsoft.Storage/storageAccounts/tableServices');
          expect(results[6].text).toEqual('Microsoft.Storage/storageAccounts/queueServices');
          expect(results[6].value).toEqual('Microsoft.Storage/storageAccounts/queueServices');
        });
    });
  });

  describe('When performing getResourceNames', () => {
    describe('and there are no special cases', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Failure Anomalies - nodeapp',
              type: 'microsoft.insights/alertrules',
            },
            {
              name: 'nodeapp',
              type: 'microsoft.insights/components',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodeapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return list of Resource Names', () => {
        return ctx.ds
          .getResourceNames('9935389e-9122-4ef9-95f9-1513dd24753f', 'nodeapp', 'microsoft.insights/components')
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('nodeapp');
            expect(results[0].value).toEqual('nodeapp');
          });
      });
    });

    describe('and the metric definition is blobServices', () => {
      const response = {
        data: {
          value: [
            {
              name: 'Failure Anomalies - nodeapp',
              type: 'microsoft.insights/alertrules',
            },
            {
              name: 'storagetest',
              type: 'Microsoft.Storage/storageAccounts',
            },
          ],
        },
        status: 200,
        statusText: 'OK',
      };

      beforeEach(() => {
        datasourceRequestMock.mockImplementation((options: { url: string }) => {
          const baseUrl =
            'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups';
          expect(options.url).toBe(baseUrl + '/nodeapp/resources?api-version=2018-01-01');
          return Promise.resolve(response);
        });
      });

      it('should return list of Resource Names', () => {
        return ctx.ds
          .getResourceNames(
            '9935389e-9122-4ef9-95f9-1513dd24753f',
            'nodeapp',
            'Microsoft.Storage/storageAccounts/blobServices'
          )
          .then((results: Array<{ text: string; value: string }>) => {
            expect(results.length).toEqual(1);
            expect(results[0].text).toEqual('storagetest/default');
            expect(results[0].value).toEqual('storagetest/default');
          });
      });
    });
  });

  describe('When performing getMetricNames', () => {
    const response = {
      data: {
        value: [
          {
            name: {
              value: 'UsedCapacity',
              localizedValue: 'Used capacity',
            },
            unit: 'CountPerSecond',
            primaryAggregationType: 'Total',
            supportedAggregationTypes: ['None', 'Average', 'Minimum', 'Maximum', 'Total', 'Count'],
            metricAvailabilities: [
              { timeGrain: 'PT1H', retention: 'P93D' },
              { timeGrain: 'PT6H', retention: 'P93D' },
              { timeGrain: 'PT12H', retention: 'P93D' },
              { timeGrain: 'P1D', retention: 'P93D' },
            ],
          },
          {
            name: {
              value: 'FreeCapacity',
              localizedValue: 'Free capacity',
            },
            unit: 'CountPerSecond',
            primaryAggregationType: 'Average',
            supportedAggregationTypes: ['None', 'Average'],
            metricAvailabilities: [
              { timeGrain: 'PT1H', retention: 'P93D' },
              { timeGrain: 'PT6H', retention: 'P93D' },
              { timeGrain: 'PT12H', retention: 'P93D' },
              { timeGrain: 'P1D', retention: 'P93D' },
            ],
          },
        ],
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        const baseUrl =
          'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp';
        const expected =
          baseUrl +
          '/providers/microsoft.insights/components/resource1' +
          '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default';
        expect(options.url).toBe(expected);
        return Promise.resolve(response);
      });
    });

    it('should return list of Metric Definitions', () => {
      return ctx.ds
        .getMetricNames(
          '9935389e-9122-4ef9-95f9-1513dd24753f',
          'nodeapp',
          'microsoft.insights/components',
          'resource1',
          'default'
        )
        .then((results: Array<{ text: string; value: string }>) => {
          expect(results.length).toEqual(2);
          expect(results[0].text).toEqual('Used capacity');
          expect(results[0].value).toEqual('UsedCapacity');
          expect(results[1].text).toEqual('Free capacity');
          expect(results[1].value).toEqual('FreeCapacity');
        });
    });
  });

  describe('When performing getMetricMetadata', () => {
    const response = {
      data: {
        value: [
          {
            name: {
              value: 'UsedCapacity',
              localizedValue: 'Used capacity',
            },
            unit: 'CountPerSecond',
            primaryAggregationType: 'Total',
            supportedAggregationTypes: ['None', 'Average', 'Minimum', 'Maximum', 'Total', 'Count'],
            metricAvailabilities: [
              { timeGrain: 'PT1H', retention: 'P93D' },
              { timeGrain: 'PT6H', retention: 'P93D' },
              { timeGrain: 'PT12H', retention: 'P93D' },
              { timeGrain: 'P1D', retention: 'P93D' },
            ],
          },
          {
            name: {
              value: 'FreeCapacity',
              localizedValue: 'Free capacity',
            },
            unit: 'CountPerSecond',
            primaryAggregationType: 'Average',
            supportedAggregationTypes: ['None', 'Average'],
            metricAvailabilities: [
              { timeGrain: 'PT1H', retention: 'P93D' },
              { timeGrain: 'PT6H', retention: 'P93D' },
              { timeGrain: 'PT12H', retention: 'P93D' },
              { timeGrain: 'P1D', retention: 'P93D' },
            ],
          },
        ],
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        const baseUrl =
          'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp';
        const expected =
          baseUrl +
          '/providers/microsoft.insights/components/resource1' +
          '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default';
        expect(options.url).toBe(expected);
        return Promise.resolve(response);
      });
    });

    it('should return Aggregation metadata for a Metric', () => {
      return ctx.ds
        .getMetricMetadata(
          '9935389e-9122-4ef9-95f9-1513dd24753f',
          'nodeapp',
          'microsoft.insights/components',
          'resource1',
          'default',
          'UsedCapacity'
        )
        .then((results: any) => {
          expect(results.primaryAggType).toEqual('Total');
          expect(results.supportedAggTypes.length).toEqual(6);
          expect(results.supportedTimeGrains.length).toEqual(4);
        });
    });
  });

  describe('When performing getMetricMetadata on metrics with dimensions', () => {
    const response = {
      data: {
        value: [
          {
            name: {
              value: 'Transactions',
              localizedValue: 'Transactions',
            },
            unit: 'Count',
            primaryAggregationType: 'Total',
            supportedAggregationTypes: ['None', 'Average', 'Minimum', 'Maximum', 'Total', 'Count'],
            isDimensionRequired: false,
            dimensions: [
              {
                value: 'ResponseType',
                localizedValue: 'Response type',
              },
              {
                value: 'GeoType',
                localizedValue: 'Geo type',
              },
              {
                value: 'ApiName',
                localizedValue: 'API name',
              },
            ],
          },
          {
            name: {
              value: 'FreeCapacity',
              localizedValue: 'Free capacity',
            },
            unit: 'CountPerSecond',
            primaryAggregationType: 'Average',
            supportedAggregationTypes: ['None', 'Average'],
          },
        ],
      },
      status: 200,
      statusText: 'OK',
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation((options: { url: string }) => {
        const baseUrl =
          'http://azuremonitor.com/azuremonitor/subscriptions/9935389e-9122-4ef9-95f9-1513dd24753f/resourceGroups/nodeapp';
        const expected =
          baseUrl +
          '/providers/microsoft.insights/components/resource1' +
          '/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=default';
        expect(options.url).toBe(expected);
        return Promise.resolve(response);
      });
    });

    it('should return dimensions for a Metric that has dimensions', () => {
      return ctx.ds
        .getMetricMetadata(
          '9935389e-9122-4ef9-95f9-1513dd24753f',
          'nodeapp',
          'microsoft.insights/components',
          'resource1',
          'default',
          'Transactions'
        )
        .then((results: any) => {
          expect(results.dimensions.length).toEqual(4);
          expect(results.dimensions[0].text).toEqual('None');
          expect(results.dimensions[0].value).toEqual('None');
          expect(results.dimensions[1].text).toEqual('Response type');
          expect(results.dimensions[1].value).toEqual('ResponseType');
        });
    });

    it('should return an empty array for a Metric that does not have dimensions', () => {
      return ctx.ds
        .getMetricMetadata(
          '9935389e-9122-4ef9-95f9-1513dd24753f',
          'nodeapp',
          'microsoft.insights/components',
          'resource1',
          'default',
          'FreeCapacity'
        )
        .then((results: any) => {
          expect(results.dimensions.length).toEqual(0);
        });
    });
  });
});
Example #28
Source File: datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('MySQLDatasource', () => {
  const instanceSettings = { name: 'mysql' };
  const templateSrv: TemplateSrv = new TemplateSrv();
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  beforeEach(() => {
    jest.clearAllMocks();
  });

  const raw = {
    from: toUtc('2018-04-25 10:00'),
    to: toUtc('2018-04-25 11:00'),
  };
  const ctx = {
    timeSrvMock: {
      timeRange: () => ({
        from: raw.from,
        to: raw.to,
        raw: raw,
      }),
    },
  } as any;

  beforeEach(() => {
    ctx.ds = new MysqlDatasource(instanceSettings, templateSrv, ctx.timeSrvMock);
  });

  describe('When performing annotationQuery', () => {
    let results: any;

    const annotationName = 'MyAnno';

    const options = {
      annotation: {
        name: annotationName,
        rawQuery: 'select time_sec, text, tags from table;',
      },
      range: {
        from: dateTime(1432288354),
        to: dateTime(1432288401),
      },
    };

    const response = {
      results: {
        MyAnno: {
          refId: annotationName,
          tables: [
            {
              columns: [{ text: 'time_sec' }, { text: 'text' }, { text: 'tags' }],
              rows: [
                [1432288355, 'some text', 'TagA,TagB'],
                [1432288390, 'some text2', ' TagB , TagC'],
                [1432288400, 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

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

    it('should return annotation list', () => {
      expect(results.length).toBe(3);

      expect(results[0].text).toBe('some text');
      expect(results[0].tags[0]).toBe('TagA');
      expect(results[0].tags[1]).toBe('TagB');

      expect(results[1].tags[0]).toBe('TagB');
      expect(results[1].tags[1]).toBe('TagC');

      expect(results[2].tags.length).toBe(0);
    });
  });

  describe('When performing metricFindQuery', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(results.length).toBe(6);
      expect(results[0].text).toBe('aTitle');
      expect(results[5].text).toBe('some text3');
    });
  });

  describe('When performing metricFindQuery with $__searchFilter and a searchFilter is given', () => {
    let results: any;
    let calledWith: any = {};
    const query = "select title from atable where title LIKE '$__searchFilter'";
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => {
        calledWith = options;
        return Promise.resolve({ data: response, status: 200 });
      });
      ctx.ds.metricFindQuery(query, { searchFilter: 'aTit' }).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(datasourceRequestMock).toBeCalledTimes(1);
      expect(calledWith.data.queries[0].rawSql).toBe("select title from atable where title LIKE 'aTit%'");
      expect(results.length).toBe(6);
    });
  });

  describe('When performing metricFindQuery with $__searchFilter but no searchFilter is given', () => {
    let results: any;
    let calledWith: any = {};
    const query = "select title from atable where title LIKE '$__searchFilter'";
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => {
        calledWith = options;
        return Promise.resolve({ data: response, status: 200 });
      });
      ctx.ds.metricFindQuery(query, {}).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(datasourceRequestMock).toBeCalledTimes(1);
      expect(calledWith.data.queries[0].rawSql).toBe("select title from atable where title LIKE '%'");
      expect(results.length).toBe(6);
    });
  });

  describe('When performing metricFindQuery with key, value columns', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: '__value' }, { text: '__text' }],
              rows: [
                ['value1', 'aTitle'],
                ['value2', 'aTitle2'],
                ['value3', 'aTitle3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(() => Promise.resolve({ data: response, status: 200 }));
      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
    });

    it('should return list of as text, value', () => {
      expect(results.length).toBe(3);
      expect(results[0].text).toBe('aTitle');
      expect(results[0].value).toBe('value1');
      expect(results[2].text).toBe('aTitle3');
      expect(results[2].value).toBe('value3');
    });
  });

  describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: '__text' }, { text: '__value' }],
              rows: [
                ['aTitle', 'same'],
                ['aTitle', 'same'],
                ['aTitle', 'diff'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(() => Promise.resolve({ data: response, status: 200 }));
      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
    });

    it('should return list of unique keys', () => {
      expect(results.length).toBe(1);
      expect(results[0].text).toBe('aTitle');
      expect(results[0].value).toBe('same');
    });
  });

  describe('When interpolating variables', () => {
    beforeEach(() => {
      ctx.variable = new CustomVariable({}, {} as any);
    });

    describe('and value is a string', () => {
      it('should return an unquoted value', () => {
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc');
      });
    });

    describe('and value is a number', () => {
      it('should return an unquoted value', () => {
        expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000);
      });
    });

    describe('and value is an array of strings', () => {
      it('should return comma separated quoted values', () => {
        expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'");
      });
    });

    describe('and variable allows multi-value and value is a string', () => {
      it('should return a quoted value', () => {
        ctx.variable.multi = true;
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
      });
    });

    describe('and variable contains single quote', () => {
      it('should return a quoted value', () => {
        ctx.variable.multi = true;
        expect(ctx.ds.interpolateVariable("a'bc", ctx.variable)).toEqual("'a''bc'");
      });
    });

    describe('and variable allows all and value is a string', () => {
      it('should return a quoted value', () => {
        ctx.variable.includeAll = true;
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
      });
    });
  });

  describe('targetContainsTemplate', () => {
    it('given query that contains template variable it should return true', () => {
      const rawSql = `SELECT
      $__timeGroup(createdAt,'$summarize') as time_sec,
      avg(value) as value,
      hostname as metric
    FROM
      grafana_metric
    WHERE
      $__timeFilter(createdAt) AND
      measurement = 'logins.count' AND
      hostname IN($host)
    GROUP BY 1, 3
    ORDER BY 1`;
      const query = {
        rawSql,
        rawQuery: true,
      };
      templateSrv.init([
        { type: 'query', name: 'summarize', current: { value: '1m' } },
        { type: 'query', name: 'host', current: { value: 'a' } },
      ]);
      expect(ctx.ds.targetContainsTemplate(query)).toBeTruthy();
    });

    it('given query that only contains global template variable it should return false', () => {
      const rawSql = `SELECT
      $__timeGroup(createdAt,'$__interval') as time_sec,
      avg(value) as value,
      hostname as metric
    FROM
      grafana_metric
    WHERE
      $__timeFilter(createdAt) AND
      measurement = 'logins.count'
    GROUP BY 1, 3
    ORDER BY 1`;
      const query = {
        rawSql,
        rawQuery: true,
      };
      templateSrv.init([
        { type: 'query', name: 'summarize', current: { value: '1m' } },
        { type: 'query', name: 'host', current: { value: 'a' } },
      ]);
      expect(ctx.ds.targetContainsTemplate(query)).toBeFalsy();
    });
  });
});
Example #29
Source File: datasource.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('PostgreSQLDatasource', () => {
  const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');

  beforeEach(() => {
    jest.clearAllMocks();
  });

  const instanceSettings = { name: 'postgresql' };

  const templateSrv: TemplateSrv = new TemplateSrv();
  const raw = {
    from: toUtc('2018-04-25 10:00'),
    to: toUtc('2018-04-25 11:00'),
  };
  const ctx = {
    timeSrvMock: {
      timeRange: () => ({
        from: raw.from,
        to: raw.to,
        raw: raw,
      }),
    },
  } as any;

  beforeEach(() => {
    ctx.ds = new PostgresDatasource(instanceSettings, templateSrv, ctx.timeSrvMock);
  });

  describe('When performing annotationQuery', () => {
    let results: any;

    const annotationName = 'MyAnno';

    const options = {
      annotation: {
        name: annotationName,
        rawQuery: 'select time, title, text, tags from table;',
      },
      range: {
        from: dateTime(1432288354),
        to: dateTime(1432288401),
      },
    };

    const response = {
      results: {
        MyAnno: {
          refId: annotationName,
          tables: [
            {
              columns: [{ text: 'time' }, { text: 'text' }, { text: 'tags' }],
              rows: [
                [1432288355, 'some text', 'TagA,TagB'],
                [1432288390, 'some text2', ' TagB , TagC'],
                [1432288400, 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

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

    it('should return annotation list', () => {
      expect(results.length).toBe(3);

      expect(results[0].text).toBe('some text');
      expect(results[0].tags[0]).toBe('TagA');
      expect(results[0].tags[1]).toBe('TagB');

      expect(results[1].tags[0]).toBe('TagB');
      expect(results[1].tags[1]).toBe('TagC');

      expect(results[2].tags.length).toBe(0);
    });
  });

  describe('When performing metricFindQuery', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(results.length).toBe(6);
      expect(results[0].text).toBe('aTitle');
      expect(results[5].text).toBe('some text3');
    });
  });

  describe('When performing metricFindQuery with $__searchFilter and a searchFilter is given', () => {
    let results: any;
    let calledWith: any = {};
    const query = "select title from atable where title LIKE '$__searchFilter'";
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => {
        calledWith = options;
        return Promise.resolve({ data: response, status: 200 });
      });

      ctx.ds.metricFindQuery(query, { searchFilter: 'aTit' }).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(datasourceRequestMock).toBeCalledTimes(1);
      expect(calledWith.data.queries[0].rawSql).toBe("select title from atable where title LIKE 'aTit%'");
      expect(results.length).toBe(6);
    });
  });

  describe('When performing metricFindQuery with $__searchFilter but no searchFilter is given', () => {
    let results: any;
    let calledWith: any = {};
    const query = "select title from atable where title LIKE '$__searchFilter'";
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: 'title' }, { text: 'text' }],
              rows: [
                ['aTitle', 'some text'],
                ['aTitle2', 'some text2'],
                ['aTitle3', 'some text3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => {
        calledWith = options;
        return Promise.resolve({ data: response, status: 200 });
      });

      ctx.ds.metricFindQuery(query, {}).then((data: any) => {
        results = data;
      });
    });

    it('should return list of all column values', () => {
      expect(datasourceRequestMock).toBeCalledTimes(1);
      expect(calledWith.data.queries[0].rawSql).toBe("select title from atable where title LIKE '%'");
      expect(results.length).toBe(6);
    });
  });

  describe('When performing metricFindQuery with key, value columns', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: '__value' }, { text: '__text' }],
              rows: [
                ['value1', 'aTitle'],
                ['value2', 'aTitle2'],
                ['value3', 'aTitle3'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
    });

    it('should return list of as text, value', () => {
      expect(results.length).toBe(3);
      expect(results[0].text).toBe('aTitle');
      expect(results[0].value).toBe('value1');
      expect(results[2].text).toBe('aTitle3');
      expect(results[2].value).toBe('value3');
    });
  });

  describe('When performing metricFindQuery with key, value columns and with duplicate keys', () => {
    let results: any;
    const query = 'select * from atable';
    const response = {
      results: {
        tempvar: {
          meta: {
            rowCount: 3,
          },
          refId: 'tempvar',
          tables: [
            {
              columns: [{ text: '__text' }, { text: '__value' }],
              rows: [
                ['aTitle', 'same'],
                ['aTitle', 'same'],
                ['aTitle', 'diff'],
              ],
            },
          ],
        },
      },
    };

    beforeEach(() => {
      datasourceRequestMock.mockImplementation(options => Promise.resolve({ data: response, status: 200 }));

      ctx.ds.metricFindQuery(query).then((data: any) => {
        results = data;
      });
      //ctx.$rootScope.$apply();
    });

    it('should return list of unique keys', () => {
      expect(results.length).toBe(1);
      expect(results[0].text).toBe('aTitle');
      expect(results[0].value).toBe('same');
    });
  });

  describe('When interpolating variables', () => {
    beforeEach(() => {
      ctx.variable = new CustomVariable({}, {} as any);
    });

    describe('and value is a string', () => {
      it('should return an unquoted value', () => {
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual('abc');
      });
    });

    describe('and value is a number', () => {
      it('should return an unquoted value', () => {
        expect(ctx.ds.interpolateVariable(1000, ctx.variable)).toEqual(1000);
      });
    });

    describe('and value is an array of strings', () => {
      it('should return comma separated quoted values', () => {
        expect(ctx.ds.interpolateVariable(['a', 'b', 'c'], ctx.variable)).toEqual("'a','b','c'");
      });
    });

    describe('and variable allows multi-value and is a string', () => {
      it('should return a quoted value', () => {
        ctx.variable.multi = true;
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
      });
    });

    describe('and variable contains single quote', () => {
      it('should return a quoted value', () => {
        ctx.variable.multi = true;
        expect(ctx.ds.interpolateVariable("a'bc", ctx.variable)).toEqual("'a''bc'");
        expect(ctx.ds.interpolateVariable("a'b'c", ctx.variable)).toEqual("'a''b''c'");
      });
    });

    describe('and variable allows all and is a string', () => {
      it('should return a quoted value', () => {
        ctx.variable.includeAll = true;
        expect(ctx.ds.interpolateVariable('abc', ctx.variable)).toEqual("'abc'");
      });
    });
  });

  describe('targetContainsTemplate', () => {
    it('given query that contains template variable it should return true', () => {
      const rawSql = `SELECT
      $__timeGroup("createdAt",'$summarize'),
      avg(value) as "value",
      hostname as "metric"
    FROM
      grafana_metric
    WHERE
      $__timeFilter("createdAt") AND
      measurement = 'logins.count' AND
      hostname IN($host)
    GROUP BY time, metric
    ORDER BY time`;
      const query = {
        rawSql,
        rawQuery: true,
      };
      templateSrv.init([
        { type: 'query', name: 'summarize', current: { value: '1m' } },
        { type: 'query', name: 'host', current: { value: 'a' } },
      ]);
      expect(ctx.ds.targetContainsTemplate(query)).toBeTruthy();
    });

    it('given query that only contains global template variable it should return false', () => {
      const rawSql = `SELECT
      $__timeGroup("createdAt",'$__interval'),
      avg(value) as "value",
      hostname as "metric"
    FROM
      grafana_metric
    WHERE
      $__timeFilter("createdAt") AND
      measurement = 'logins.count'
    GROUP BY time, metric
    ORDER BY time`;
      const query = {
        rawSql,
        rawQuery: true,
      };
      templateSrv.init([
        { type: 'query', name: 'summarize', current: { value: '1m' } },
        { type: 'query', name: 'host', current: { value: 'a' } },
      ]);
      expect(ctx.ds.targetContainsTemplate(query)).toBeFalsy();
    });
  });
});