@grafana/data#toDataFrame TypeScript Examples

The following examples show how to use @grafana/data#toDataFrame. 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: utils.test.ts    From grafana-chinese with Apache License 2.0 7 votes vote down vote up
aSeries = toDataFrame({
  fields: [
    { name: 'time', type: FieldType.time, values: [100, 200, 300] },
    {
      name: 'value',
      type: FieldType.number,
      values: [10, 20, 10],
      config: { color: { mode: FieldColorMode.Fixed, fixedColor: 'red' } },
    },
  ],
})
Example #2
Source File: utils.test.ts    From grafana-chinese with Apache License 2.0 7 votes vote down vote up
bSeries = toDataFrame({
  fields: [
    { name: 'time', type: FieldType.time, values: [100, 200, 300] },
    {
      name: 'value',
      type: FieldType.number,
      values: [30, 60, 30],
      config: { color: { mode: FieldColorMode.Fixed, fixedColor: 'blue' } },
    },
  ],
})
Example #3
Source File: utils.test.ts    From grafana-chinese with Apache License 2.0 7 votes vote down vote up
cSeries = toDataFrame({
  fields: [
    { name: 'time', type: FieldType.time, values: [100, 300] },
    {
      name: 'value',
      type: FieldType.number,
      values: [30, 30],
      config: { color: { mode: FieldColorMode.Fixed, fixedColor: 'yellow' } },
    },
  ],
})
Example #4
Source File: utils.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
export function dataFrameToCSV(dto?: DataFrameDTO[]) {
  if (!dto || !dto.length) {
    return '';
  }
  return toCSV(dto.map(v => toDataFrame(v)));
}
Example #5
Source File: InputDatasource.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
query(options: DataQueryRequest<InputQuery>): Promise<DataQueryResponse> {
    const results: DataFrame[] = [];
    for (const query of options.targets) {
      if (query.hide) {
        continue;
      }
      let data = this.data;
      if (query.data) {
        data = query.data.map(v => toDataFrame(v));
      }
      for (let i = 0; i < data.length; i++) {
        results.push({
          ...data[i],
          refId: query.refId,
        });
      }
    }
    return Promise.resolve({ data: results });
  }
Example #6
Source File: datasource.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
convertResponseToDataFrames = (result: any): DataQueryResponse => {
    const data: DataFrame[] = [];
    if (!result || !result.data) {
      return { data };
    }
    // Series are either at the root or under a node called 'series'
    const series = result.data.series || result.data;
    if (!_.isArray(series)) {
      throw { message: 'Missing series in result', data: result };
    }

    for (let i = 0; i < series.length; i++) {
      const s = series[i];
      for (let y = 0; y < s.datapoints.length; y++) {
        s.datapoints[y][1] *= 1000;
      }
      const frame = toDataFrame(s);

      // Metrictank metadata
      if (s.meta) {
        frame.meta = {
          custom: {
            request: result.data.meta, // info for the whole request
            info: s.meta, // Array of metadata
          },
        };
      }
      data.push(frame);
    }
    return { data };
  };
Example #7
Source File: runRequest.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
/**
 * All panels will be passed tables that have our best guess at colum type set
 *
 * This is also used by PanelChrome for snapshot support
 */
export function getProcessedDataFrames(results?: DataQueryResponseData[]): DataFrame[] {
  if (!isArray(results)) {
    return [];
  }

  const dataFrames: DataFrame[] = [];

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

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

    dataFrames.push(dataFrame);
  }

  return dataFrames;
}
Example #8
Source File: QueryEditorRow.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
describe('filterPanelDataToQuery', () => {
  const data: PanelData = {
    state: LoadingState.Done,
    series: [
      toDataFrame({ refId: 'A', fields: [{ name: 'AAA' }], meta: {} }),
      toDataFrame({ refId: 'B', fields: [{ name: 'B111' }], meta: {} }),
      toDataFrame({ refId: 'B', fields: [{ name: 'B222' }], meta: {} }),
      toDataFrame({ refId: 'B', fields: [{ name: 'B333' }], meta: {} }),
      toDataFrame({ refId: 'C', fields: [{ name: 'CCCC' }], meta: { requestId: 'sub3' } }),
    ],
    error: {
      refId: 'B',
      message: 'Error!!',
    },
    request: makePretendRequest('111', [
      makePretendRequest('sub1'),
      makePretendRequest('sub2'),
      makePretendRequest('sub3'),
    ]),
    timeRange: { from: dateTime(), to: dateTime(), raw: { from: 'now-1d', to: 'now' } },
  };

  it('should not have an error unless the refId matches', () => {
    const panelData = filterPanelDataToQuery(data, 'A');
    expect(panelData.series.length).toBe(1);
    expect(panelData.series[0].refId).toBe('A');
    expect(panelData.error).toBeUndefined();
  });

  it('should match the error to the query', () => {
    const panelData = filterPanelDataToQuery(data, 'B');
    expect(panelData.series.length).toBe(3);
    expect(panelData.series[0].refId).toBe('B');
    expect(panelData.error!.refId).toBe('B');
  });
});
Example #9
Source File: ResultProcessor.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
testContext = (options: any = {}) => {
  const timeSeries = toDataFrame({
    name: 'A-series',
    refId: 'A',
    fields: [
      { name: 'A-series', type: FieldType.number, values: [4, 5, 6] },
      { name: 'time', type: FieldType.time, values: [100, 200, 300] },
    ],
  });

  const table = toDataFrame({
    name: 'table-res',
    refId: 'A',
    fields: [
      { name: 'value', type: FieldType.number, values: [4, 5, 6] },
      { name: 'time', type: FieldType.time, values: [100, 200, 300] },
      { name: 'message', type: FieldType.string, values: ['this is a message', 'second message', 'third'] },
    ],
  });

  const emptyTable = toDataFrame({ name: 'empty-table', refId: 'A', fields: [] });

  const defaultOptions = {
    mode: ExploreMode.Metrics,
    dataFrames: [timeSeries, table, emptyTable],
    graphResult: [] as TimeSeries[],
    tableResult: new TableModel(),
    logsResult: { hasUniqueLabels: false, rows: [] as LogRowModel[] },
  };

  const combinedOptions = { ...defaultOptions, ...options };

  const state = ({
    mode: combinedOptions.mode,
    graphResult: combinedOptions.graphResult,
    tableResult: combinedOptions.tableResult,
    logsResult: combinedOptions.logsResult,
    queryIntervals: { intervalMs: 10 },
  } as any) as ExploreItemState;

  const resultProcessor = new ResultProcessor(state, combinedOptions.dataFrames, 60000, 'utc');

  return {
    dataFrames: combinedOptions.dataFrames,
    resultProcessor,
  };
}
Example #10
Source File: ResultProcessor.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
getTableResult(): DataFrame | null {
    if (this.state.mode !== ExploreMode.Metrics) {
      return null;
    }

    // For now ignore time series
    // We can change this later, just need to figure out how to
    // Ignore time series only for prometheus
    const onlyTables = this.dataFrames.filter(frame => !isTimeSeries(frame));

    if (onlyTables.length === 0) {
      return null;
    }

    const tables = onlyTables.map(frame => {
      const { fields } = frame;
      const fieldCount = fields.length;
      const rowCount = frame.length;

      const columns = fields.map(field => ({
        text: field.name,
        type: field.type,
        filterable: field.config.filterable,
      }));

      const rows: any[][] = [];
      for (let i = 0; i < rowCount; i++) {
        const row: any[] = [];
        for (let j = 0; j < fieldCount; j++) {
          row.push(frame.fields[j].values.get(i));
        }
        rows.push(row);
      }

      return new TableModel({
        columns,
        rows,
        meta: frame.meta,
      });
    });

    const mergedTable = mergeTablesIntoModel(new TableModel(), ...tables);
    const data = toDataFrame(mergedTable);

    // set display processor
    for (const field of data.fields) {
      field.display = getDisplayProcessor({
        field,
        theme: config.theme,
      });
    }

    return data;
  }
Example #11
Source File: fieldDisplayValuesProxy.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
describe('getFieldDisplayValuesProxy', () => {
  const data = applyFieldOverrides({
    data: [
      toDataFrame({
        fields: [
          { name: 'Time', values: [1, 2, 3] },
          {
            name: 'power',
            values: [100, 200, 300],
            config: {
              title: 'The Power',
            },
          },
          {
            name: 'Last',
            values: ['a', 'b', 'c'],
          },
        ],
      }),
    ],
    fieldOptions: {
      defaults: {},
      overrides: [],
    },
    replaceVariables: (val: string) => val,
    timeZone: 'utc',
    theme: {} as GrafanaTheme,
    autoMinMax: true,
  })[0];

  it('should define all display functions', () => {
    // Field display should be set
    for (const field of data.fields) {
      expect(field.display).toBeDefined();
    }
  });

  it('should format the time values in UTC', () => {
    // Test Proxies in general
    const p = getFieldDisplayValuesProxy(data, 0);
    const time = p.Time;
    expect(time.numeric).toEqual(1);
    expect(time.text).toEqual('1970-01-01 00:00:00');

    // Should get to the same values by name or index
    const time2 = p[0];
    expect(time2.toString()).toEqual(time.toString());
  });

  it('Lookup by name, index, or title', () => {
    const p = getFieldDisplayValuesProxy(data, 2);
    expect(p.power.numeric).toEqual(300);
    expect(p['power'].numeric).toEqual(300);
    expect(p['The Power'].numeric).toEqual(300);
    expect(p[1].numeric).toEqual(300);
  });

  it('should return undefined when missing', () => {
    const p = getFieldDisplayValuesProxy(data, 0);
    expect(p.xyz).toBeUndefined();
    expect(p[100]).toBeUndefined();
  });
});
Example #12
Source File: datasource.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
performTimeSeriesQuery(request: any, { from, to }: TimeRange): Promise<any> {
    return this.awsRequest('/api/tsdb/query', request)
      .then((res: any) => {
        if (!res.results) {
          return { data: [] };
        }
        return Object.values(request.queries).reduce(
          ({ data, error }: any, queryRequest: any) => {
            const queryResult = res.results[queryRequest.refId];
            if (!queryResult) {
              return { data, error };
            }

            const link = this.buildCloudwatchConsoleUrl(
              queryRequest,
              from.toISOString(),
              to.toISOString(),
              queryRequest.refId,
              queryResult.meta.gmdMeta
            );

            return {
              error: error || queryResult.error ? { message: queryResult.error } : null,
              data: [
                ...data,
                ...queryResult.series.map(({ name, points }: any) => {
                  const dataFrame = toDataFrame({
                    target: name,
                    datapoints: points,
                    refId: queryRequest.refId,
                    meta: queryResult.meta,
                  });
                  if (link) {
                    for (const field of dataFrame.fields) {
                      field.config.links = [
                        {
                          url: link,
                          title: 'View in CloudWatch console',
                          targetBlank: true,
                        },
                      ];
                    }
                  }
                  return dataFrame;
                }),
              ],
            };
          },
          { data: [], error: null }
        );
      })
      .catch((err: any = { data: { error: '' } }) => {
        if (/^Throttling:.*/.test(err.data.message)) {
          const failedRedIds = Object.keys(err.data.results);
          const regionsAffected = Object.values(request.queries).reduce(
            (res: string[], { refId, region }: CloudWatchQuery) =>
              !failedRedIds.includes(refId) || res.includes(region) ? res : [...res, region],
            []
          ) as string[];

          regionsAffected.forEach(region => this.debouncedAlert(this.datasourceName, this.getActualRegion(region)));
        }

        if (err.data && err.data.message === 'Metric request error' && err.data.error) {
          err.data.message = err.data.error;
        }

        throw err;
      });
  }
Example #13
Source File: elastic_response.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
getLogs(logMessageField?: string, logLevelField?: string): DataQueryResponse {
    const dataFrame: DataFrame[] = [];

    for (let n = 0; n < this.response.responses.length; n++) {
      const response = this.response.responses[n];
      if (response.error) {
        throw this.getErrorFromElasticResponse(this.response, response.error);
      }

      const { propNames, docs } = flattenHits(response.hits.hits);
      if (docs.length > 0) {
        const series = createEmptyDataFrame(propNames, this.targets[0].timeField, logMessageField, logLevelField);

        // Add a row for each document
        for (const doc of docs) {
          if (logLevelField) {
            // Remap level field based on the datasource config. This field is then used in explore to figure out the
            // log level. We may rewrite some actual data in the level field if they are different.
            doc['level'] = doc[logLevelField];
          }

          series.add(doc);
        }

        dataFrame.push(series);
      }

      if (response.aggregations) {
        const aggregations = response.aggregations;
        const target = this.targets[n];
        const tmpSeriesList: any[] = [];
        const table = new TableModel();

        this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0);
        this.trimDatapoints(tmpSeriesList, target);
        this.nameSeries(tmpSeriesList, target);

        for (let y = 0; y < tmpSeriesList.length; y++) {
          const series = toDataFrame(tmpSeriesList[y]);
          dataFrame.push(series);
        }
      }
    }

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

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

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

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

        if (!queryRes.series) {
          return;
        }

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

    return Promise.resolve([]);
  }
Example #15
Source File: LogRowContextProvider.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
getRowContexts = async (
  getRowContext: (row: LogRowModel, options?: any) => Promise<DataQueryResponse>,
  row: LogRowModel,
  limit: number
) => {
  const promises = [
    getRowContext(row, {
      limit,
    }),
    getRowContext(row, {
      // The start time is inclusive so we will get the one row we are using as context entry
      limit: limit + 1,
      direction: 'FORWARD',
    }),
  ];

  const results: Array<DataQueryResponse | DataQueryError> = await Promise.all(promises.map(p => p.catch(e => e)));

  return {
    data: results.map(result => {
      const dataResult: DataQueryResponse = result as DataQueryResponse;
      if (!dataResult.data) {
        return [];
      }

      const data: any[] = [];
      for (let index = 0; index < dataResult.data.length; index++) {
        const dataFrame = toDataFrame(dataResult.data[index]);
        const fieldCache = new FieldCache(dataFrame);
        const timestampField: Field<string> = fieldCache.getFieldByName('ts')!;
        const idField: Field<string> | undefined = fieldCache.getFieldByName('id');

        for (let fieldIndex = 0; fieldIndex < timestampField.values.length; fieldIndex++) {
          // TODO: this filtering is datasource dependant so it will make sense to move it there so the API is
          //  to return correct list of lines handling inclusive ranges or how to filter the correct line on the
          //  datasource.

          // Filter out the row that is the one used as a focal point for the context as we will get it in one of the
          // requests.
          if (idField) {
            // For Loki this means we filter only the one row. Issue is we could have other rows logged at the same
            // ns which came before but they come in the response that search for logs after. This means right now
            // we will show those as if they came after. This is not strictly correct but seems better than loosing them
            // and making this correct would mean quite a bit of complexity to shuffle things around and messing up
            //counts.
            if (idField.values.get(fieldIndex) === row.uid) {
              continue;
            }
          } else {
            // Fallback to timestamp. This should not happen right now as this feature is implemented only for loki
            // and that has ID. Later this branch could be used in other DS but mind that this could also filter out
            // logs which were logged in the same timestamp and that can be a problem depending on the precision.
            if (parseInt(timestampField.values.get(fieldIndex), 10) === row.timeEpochMs) {
              continue;
            }
          }

          const lineField: Field<string> = dataFrame.fields.filter(field => field.name === 'line')[0];
          const line = lineField.values.get(fieldIndex); // assuming that both fields have same length

          if (data.length === 0) {
            data[0] = [line];
          } else {
            data[0].push(line);
          }
        }
      }

      return data;
    }),
    errors: results.map(result => {
      const errorResult: DataQueryError = result as DataQueryError;
      if (!errorResult.message) {
        return '';
      }

      return errorResult.message;
    }),
  };
}
Example #16
Source File: InputDatasource.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
constructor(instanceSettings: DataSourceInstanceSettings<InputOptions>) {
    super(instanceSettings);

    if (instanceSettings.jsonData.data) {
      this.data = instanceSettings.jsonData.data.map(v => toDataFrame(v));
    }
  }
Example #17
Source File: logs_model.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number, timeZone: TimeZone): GraphSeriesXY[] {
  // currently interval is rangeMs / resolution, which is too low for showing series as bars.
  // need at least 10px per bucket, so we multiply interval by 10. Should be solved higher up the chain
  // when executing queries & interval calculated and not here but this is a temporary fix.
  // intervalMs = intervalMs * 10;

  // Graph time series by log level
  const seriesByLevel: any = {};
  const bucketSize = intervalMs * 10;
  const seriesList: any[] = [];

  const sortedRows = rows.sort(sortInAscendingOrder);
  for (const row of sortedRows) {
    let series = seriesByLevel[row.logLevel];

    if (!series) {
      seriesByLevel[row.logLevel] = series = {
        lastTs: null,
        datapoints: [],
        alias: row.logLevel,
        target: row.logLevel,
        color: LogLevelColor[row.logLevel],
      };

      seriesList.push(series);
    }

    // align time to bucket size - used Math.floor for calculation as time of the bucket
    // must be in the past (before Date.now()) to be displayed on the graph
    const time = Math.floor(row.timeEpochMs / bucketSize) * bucketSize;

    // Entry for time
    if (time === series.lastTs) {
      series.datapoints[series.datapoints.length - 1][0]++;
    } else {
      series.datapoints.push([1, time]);
      series.lastTs = time;
    }

    // add zero to other levels to aid stacking so each level series has same number of points
    for (const other of seriesList) {
      if (other !== series && other.lastTs !== time) {
        other.datapoints.push([0, time]);
        other.lastTs = time;
      }
    }
  }

  return seriesList.map((series, i) => {
    series.datapoints.sort((a: number[], b: number[]) => {
      return a[1] - b[1];
    });

    // EEEP: converts GraphSeriesXY to DataFrame and back again!
    const data = toDataFrame(series);
    const points = getFlotPairs({
      xField: data.fields[1],
      yField: data.fields[0],
      nullValueMode: NullValueMode.Null,
    });

    const timeField = data.fields[1];
    timeField.display = getDisplayProcessor({
      field: timeField,
      timeZone,
    });

    const valueField = data.fields[0];
    valueField.config = {
      ...valueField.config,
      color: series.color,
    };

    const graphSeries: GraphSeriesXY = {
      color: series.color,
      label: series.alias,
      data: points,
      isVisible: true,
      yAxis: {
        index: 1,
        min: 0,
        tickDecimals: 0,
      },
      seriesIndex: i,
      timeField,
      valueField,
      // for now setting the time step to be 0,
      // and handle the bar width by setting lineWidth instead of barWidth in flot options
      timeStep: 0,
    };

    return graphSeries;
  });
}
Example #18
Source File: reducers.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('Explore item reducer', () => {
  describe('scanning', () => {
    it('should start scanning', () => {
      const initialState = {
        ...makeExploreItemState(),
        scanning: false,
      };

      reducerTester<ExploreItemState>()
        .givenReducer(itemReducer, initialState)
        .whenActionIsDispatched(scanStartAction({ exploreId: ExploreId.left }))
        .thenStateShouldEqual({
          ...makeExploreItemState(),
          scanning: true,
        });
    });
    it('should stop scanning', () => {
      const initialState = {
        ...makeExploreItemState(),
        scanning: true,
        scanRange: {} as RawTimeRange,
      };

      reducerTester<ExploreItemState>()
        .givenReducer(itemReducer, initialState)
        .whenActionIsDispatched(scanStopAction({ exploreId: ExploreId.left }))
        .thenStateShouldEqual({
          ...makeExploreItemState(),
          scanning: false,
          scanRange: undefined,
        });
    });
  });

  describe('changing datasource', () => {
    describe('when changeMode is dispatched', () => {
      it('then it should set correct state', () => {
        reducerTester<ExploreItemState>()
          .givenReducer(itemReducer, ({} as unknown) as ExploreItemState)
          .whenActionIsDispatched(changeModeAction({ exploreId: ExploreId.left, mode: ExploreMode.Logs }))
          .thenStatePredicateShouldEqual((resultingState: ExploreItemState) => {
            expect(resultingState.mode).toEqual(ExploreMode.Logs);
            expect(resultingState.logsResult).toBeNull();
            expect(resultingState.graphResult).toBeNull();
            expect(resultingState.tableResult).toBeNull();
            return true;
          });
      });
    });

    describe('when updateDatasourceInstanceAction is dispatched', () => {
      describe('and datasourceInstance supports graph, logs, table and has a startpage', () => {
        it('then it should set correct state', () => {
          const StartPage = {};
          const datasourceInstance = {
            meta: {
              metrics: true,
              logs: true,
            },
            components: {
              ExploreStartPage: StartPage,
            },
          } as DataSourceApi;
          const queries: DataQuery[] = [];
          const queryKeys: string[] = [];
          const initialState: ExploreItemState = ({
            datasourceInstance: null,
            queries,
            queryKeys,
          } as unknown) as ExploreItemState;
          const expectedState: any = {
            datasourceInstance,
            queries,
            queryKeys,
            graphResult: null,
            logsResult: null,
            tableResult: null,
            supportedModes: [ExploreMode.Metrics, ExploreMode.Logs],
            mode: ExploreMode.Metrics,
            latency: 0,
            loading: false,
            queryResponse: createEmptyQueryResponse(),
          };

          reducerTester<ExploreItemState>()
            .givenReducer(itemReducer, initialState)
            .whenActionIsDispatched(updateDatasourceInstanceAction({ exploreId: ExploreId.left, datasourceInstance }))
            .thenStateShouldEqual(expectedState);
        });
      });
    });
  });

  describe('changing refresh intervals', () => {
    it("should result in 'streaming' state, when live-tailing is active", () => {
      const initialState = makeExploreItemState();
      const expectedState = {
        ...makeExploreItemState(),
        refreshInterval: 'LIVE',
        isLive: true,
        loading: true,
        logsResult: {
          hasUniqueLabels: false,
          rows: [] as any[],
        },
        queryResponse: {
          ...makeExploreItemState().queryResponse,
          state: LoadingState.Streaming,
        },
      };
      reducerTester<ExploreItemState>()
        .givenReducer(itemReducer, initialState)
        .whenActionIsDispatched(changeRefreshIntervalAction({ exploreId: ExploreId.left, refreshInterval: 'LIVE' }))
        .thenStateShouldEqual(expectedState);
    });

    it("should result in 'done' state, when live-tailing is stopped", () => {
      const initialState = makeExploreItemState();
      const expectedState = {
        ...makeExploreItemState(),
        refreshInterval: '',
        logsResult: {
          hasUniqueLabels: false,
          rows: [] as any[],
        },
        queryResponse: {
          ...makeExploreItemState().queryResponse,
          state: LoadingState.Done,
        },
      };
      reducerTester<ExploreItemState>()
        .givenReducer(itemReducer, initialState)
        .whenActionIsDispatched(changeRefreshIntervalAction({ exploreId: ExploreId.left, refreshInterval: '' }))
        .thenStateShouldEqual(expectedState);
    });
  });

  describe('toggling panels', () => {
    describe('when toggleGraphAction is dispatched', () => {
      it('then it should set correct state', () => {
        reducerTester<ExploreItemState>()
          .givenReducer(itemReducer, ({ graphResult: [] } as unknown) as ExploreItemState)
          .whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
          .thenStateShouldEqual(({ showingGraph: true, graphResult: [] } as unknown) as ExploreItemState)
          .whenActionIsDispatched(toggleGraphAction({ exploreId: ExploreId.left }))
          .thenStateShouldEqual(({ showingGraph: false, graphResult: null } as unknown) as ExploreItemState);
      });
    });

    describe('when toggleTableAction is dispatched', () => {
      it('then it should set correct state', () => {
        const table = toDataFrame({
          name: 'logs',
          fields: [
            {
              name: 'time',
              type: 'number',
              values: [1, 2],
            },
          ],
        });

        reducerTester<ExploreItemState>()
          .givenReducer(itemReducer, ({ tableResult: table } as unknown) as ExploreItemState)
          .whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
          .thenStateShouldEqual(({ showingTable: true, tableResult: table } as unknown) as ExploreItemState)
          .whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left }))
          .thenStateShouldEqual(({ showingTable: false, tableResult: null } as unknown) as ExploreItemState);
      });
    });
  });

  describe('changing range', () => {
    describe('when changeRangeAction is dispatched', () => {
      it('then it should set correct state', () => {
        reducerTester<ExploreItemState>()
          .givenReducer(itemReducer, ({
            update: { ...makeInitialUpdateState(), range: true },
            range: null,
            absoluteRange: null,
          } as unknown) as ExploreItemState)
          .whenActionIsDispatched(
            changeRangeAction({
              exploreId: ExploreId.left,
              absoluteRange: { from: 1546297200000, to: 1546383600000 },
              range: { from: dateTime('2019-01-01'), to: dateTime('2019-01-02'), raw: { from: 'now-1d', to: 'now' } },
            })
          )
          .thenStateShouldEqual(({
            update: { ...makeInitialUpdateState(), range: false },
            absoluteRange: { from: 1546297200000, to: 1546383600000 },
            range: { from: dateTime('2019-01-01'), to: dateTime('2019-01-02'), raw: { from: 'now-1d', to: 'now' } },
          } as unknown) as ExploreItemState);
      });
    });
  });
});
Example #19
Source File: ResultProcessor.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('ResultProcessor', () => {
  describe('constructed without result', () => {
    describe('when calling getGraphResult', () => {
      it('then it should return null', () => {
        const { resultProcessor } = testContext({ dataFrames: [] });
        const theResult = resultProcessor.getGraphResult();

        expect(theResult).toEqual(null);
      });
    });

    describe('when calling getTableResult', () => {
      it('then it should return null', () => {
        const { resultProcessor } = testContext({ dataFrames: [] });
        const theResult = resultProcessor.getTableResult();

        expect(theResult).toEqual(null);
      });
    });

    describe('when calling getLogsResult', () => {
      it('then it should return null', () => {
        const { resultProcessor } = testContext({ dataFrames: [] });
        const theResult = resultProcessor.getLogsResult();

        expect(theResult).toBeNull();
      });
    });
  });

  describe('constructed with a result that is a DataQueryResponse', () => {
    describe('when calling getGraphResult', () => {
      it('then it should return correct graph result', () => {
        const { resultProcessor, dataFrames } = testContext();
        const timeField = dataFrames[0].fields[1];
        const valueField = dataFrames[0].fields[0];
        const theResult = resultProcessor.getGraphResult();

        expect(theResult).toEqual([
          {
            label: 'A-series',
            color: '#7EB26D',
            data: [
              [100, 4],
              [200, 5],
              [300, 6],
            ],
            info: undefined,
            isVisible: true,
            yAxis: {
              index: 1,
            },
            seriesIndex: 0,
            timeField,
            valueField,
            timeStep: 100,
          },
        ]);
      });
    });

    describe('when calling getTableResult', () => {
      it('then it should return correct table result', () => {
        const { resultProcessor } = testContext();
        let theResult = resultProcessor.getTableResult();
        expect(theResult.fields[0].name).toEqual('value');
        expect(theResult.fields[1].name).toEqual('time');
        expect(theResult.fields[2].name).toEqual('message');
        expect(theResult.fields[1].display).not.toBeNull();
        expect(theResult.length).toBe(3);

        // Same data though a DataFrame
        theResult = toDataFrame(
          new TableModel({
            columns: [
              { text: 'value', type: 'number' },
              { text: 'time', type: 'time' },
              { text: 'message', type: 'string' },
            ],
            rows: [
              [4, 100, 'this is a message'],
              [5, 200, 'second message'],
              [6, 300, 'third'],
            ],
            type: 'table',
          })
        );
        expect(theResult.fields[0].name).toEqual('value');
        expect(theResult.fields[1].name).toEqual('time');
        expect(theResult.fields[2].name).toEqual('message');
        expect(theResult.fields[1].display).not.toBeNull();
        expect(theResult.length).toBe(3);
      });
    });

    describe('when calling getLogsResult', () => {
      it('then it should return correct logs result', () => {
        const { resultProcessor, dataFrames } = testContext({ mode: ExploreMode.Logs });
        const timeField = dataFrames[0].fields[1];
        const valueField = dataFrames[0].fields[0];
        const logsDataFrame = dataFrames[1];
        const theResult = resultProcessor.getLogsResult();

        expect(theResult).toEqual({
          hasUniqueLabels: false,
          meta: [],
          rows: [
            {
              rowIndex: 2,
              dataFrame: logsDataFrame,
              entry: 'third',
              entryFieldIndex: 2,
              hasAnsi: false,
              labels: undefined,
              logLevel: 'unknown',
              raw: 'third',
              searchWords: [] as string[],
              timeEpochMs: 300,
              timeFromNow: 'fromNow() jest mocked',
              timeLocal: 'format() jest mocked',
              timeUtc: 'format() jest mocked',
              uid: '2',
              uniqueLabels: {},
            },
            {
              rowIndex: 1,
              dataFrame: logsDataFrame,
              entry: 'second message',
              entryFieldIndex: 2,
              hasAnsi: false,
              labels: undefined,
              logLevel: 'unknown',
              raw: 'second message',
              searchWords: [] as string[],
              timeEpochMs: 200,
              timeFromNow: 'fromNow() jest mocked',
              timeLocal: 'format() jest mocked',
              timeUtc: 'format() jest mocked',
              uid: '1',
              uniqueLabels: {},
            },
            {
              rowIndex: 0,
              dataFrame: logsDataFrame,
              entry: 'this is a message',
              entryFieldIndex: 2,
              hasAnsi: false,
              labels: undefined,
              logLevel: 'unknown',
              raw: 'this is a message',
              searchWords: [] as string[],
              timeEpochMs: 100,
              timeFromNow: 'fromNow() jest mocked',
              timeLocal: 'format() jest mocked',
              timeUtc: 'format() jest mocked',
              uid: '0',
              uniqueLabels: {},
            },
          ],
          series: [
            {
              label: 'A-series',
              color: '#7EB26D',
              data: [
                [100, 4],
                [200, 5],
                [300, 6],
              ],
              info: undefined,
              isVisible: true,
              yAxis: {
                index: 1,
              },
              seriesIndex: 0,
              timeField,
              valueField,
              timeStep: 100,
            },
          ],
        });
      });
    });
  });
});
Example #20
Source File: linkSuppliers.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('getLinksFromLogsField', () => {
  let originalLinkSrv: LinkService;
  beforeAll(() => {
    // We do not need more here and TimeSrv is hard to setup fully.
    const timeSrvMock: TimeSrv = {
      timeRangeForUrl() {
        const from = dateTime().subtract(1, 'h');
        const to = dateTime();
        return { from, to, raw: { from, to } };
      },
    } as any;
    const linkService = new LinkSrv(new TemplateSrv(), timeSrvMock);
    originalLinkSrv = getLinkSrv();
    setLinkSrv(linkService);
  });

  afterAll(() => {
    setLinkSrv(originalLinkSrv);
  });

  it('interpolates link from field', () => {
    const field: Field = {
      name: 'test field',
      type: FieldType.number,
      config: {
        links: [
          {
            title: 'title1',
            url: 'http://domain.com/${__value.raw}',
          },
          {
            title: 'title2',
            url: 'http://anotherdomain.sk/${__value.raw}',
          },
        ],
      },
      values: new ArrayVector([1, 2, 3]),
    };
    const links = getLinksFromLogsField(field, 2);
    expect(links.length).toBe(2);
    expect(links[0].href).toBe('http://domain.com/3');
    expect(links[1].href).toBe('http://anotherdomain.sk/3');
  });

  it('handles zero links', () => {
    const field: Field = {
      name: 'test field',
      type: FieldType.number,
      config: {},
      values: new ArrayVector([1, 2, 3]),
    };
    const links = getLinksFromLogsField(field, 2);
    expect(links.length).toBe(0);
  });

  it('links to items on the row', () => {
    const data = applyFieldOverrides({
      data: [
        toDataFrame({
          name: 'Hello Templates',
          refId: 'ZZZ',
          fields: [
            { name: 'Time', values: [1, 2, 3] },
            {
              name: 'Power',
              values: [100.2000001, 200, 300],
              config: {
                unit: 'kW',
                decimals: 3,
                title: 'TheTitle',
              },
            },
            {
              name: 'Last',
              values: ['a', 'b', 'c'],
              config: {
                links: [
                  {
                    title: 'By Name',
                    url: 'http://go/${__data.fields.Power}',
                  },
                  {
                    title: 'By Index',
                    url: 'http://go/${__data.fields[1]}',
                  },
                  {
                    title: 'By Title',
                    url: 'http://go/${__data.fields[TheTitle]}',
                  },
                  {
                    title: 'Numeric Value',
                    url: 'http://go/${__data.fields.Power.numeric}',
                  },
                  {
                    title: 'Text (no suffix)',
                    url: 'http://go/${__data.fields.Power.text}',
                  },
                  {
                    title: 'Unknown Field',
                    url: 'http://go/${__data.fields.XYZ}',
                  },
                  {
                    title: 'Data Frame name',
                    url: 'http://go/${__data.name}',
                  },
                  {
                    title: 'Data Frame refId',
                    url: 'http://go/${__data.refId}',
                  },
                ],
              },
            },
          ],
        }),
      ],
      fieldOptions: {
        defaults: {},
        overrides: [],
      },
      replaceVariables: (val: string) => val,
      timeZone: 'utc',
      theme: {} as GrafanaTheme,
      autoMinMax: true,
    })[0];

    const rowIndex = 0;
    const colIndex = data.fields.length - 1;
    const field = data.fields[colIndex];
    const fieldDisp: FieldDisplay = {
      name: 'hello',
      field: field.config,
      view: new DataFrameView(data),
      rowIndex,
      colIndex,
      display: field.display!(field.values.get(rowIndex)),
    };

    const supplier = getFieldLinksSupplier(fieldDisp);
    const links = supplier.getLinks({}).map(m => {
      return {
        title: m.title,
        href: m.href,
      };
    });
    expect(links).toMatchInlineSnapshot(`
      Array [
        Object {
          "href": "http://go/100.200 kW",
          "title": "By Name",
        },
        Object {
          "href": "http://go/100.200 kW",
          "title": "By Index",
        },
        Object {
          "href": "http://go/100.200 kW",
          "title": "By Title",
        },
        Object {
          "href": "http://go/100.2000001",
          "title": "Numeric Value",
        },
        Object {
          "href": "http://go/100.200",
          "title": "Text (no suffix)",
        },
        Object {
          "href": "http://go/\${__data.fields.XYZ}",
          "title": "Unknown Field",
        },
        Object {
          "href": "http://go/Hello Templates",
          "title": "Data Frame name",
        },
        Object {
          "href": "http://go/ZZZ",
          "title": "Data Frame refId",
        },
      ]
    `);
  });
});
Example #21
Source File: azure_monitor_datasource.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
async query(options: DataQueryRequest<AzureMonitorQuery>): Promise<DataQueryResponseData[]> {
    const queries = _.filter(options.targets, item => {
      return (
        item.hide !== true &&
        item.azureMonitor.resourceGroup &&
        item.azureMonitor.resourceGroup !== this.defaultDropdownValue &&
        item.azureMonitor.resourceName &&
        item.azureMonitor.resourceName !== this.defaultDropdownValue &&
        item.azureMonitor.metricDefinition &&
        item.azureMonitor.metricDefinition !== this.defaultDropdownValue &&
        item.azureMonitor.metricName &&
        item.azureMonitor.metricName !== this.defaultDropdownValue
      );
    }).map(target => {
      const item = target.azureMonitor;

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

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

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

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

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

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

    return Promise.resolve([]);
  }
Example #22
Source File: logs_model.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('dataFrameToLogsModel', () => {
  it('given empty series should return empty logs model', () => {
    expect(dataFrameToLogsModel([] as DataFrame[], 0, 'utc')).toMatchObject(emptyLogsModel);
  });

  it('given series without correct series name should return empty logs model', () => {
    const series: DataFrame[] = [
      toDataFrame({
        fields: [],
      }),
    ];
    expect(dataFrameToLogsModel(series, 0, 'utc')).toMatchObject(emptyLogsModel);
  });

  it('given series without a time field should return empty logs model', () => {
    const series: DataFrame[] = [
      new MutableDataFrame({
        fields: [
          {
            name: 'message',
            type: FieldType.string,
            values: [],
          },
        ],
      }),
    ];
    expect(dataFrameToLogsModel(series, 0, 'utc')).toMatchObject(emptyLogsModel);
  });

  it('given series without a string field should return empty logs model', () => {
    const series: DataFrame[] = [
      new MutableDataFrame({
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: [],
          },
        ],
      }),
    ];
    expect(dataFrameToLogsModel(series, 0, 'utc')).toMatchObject(emptyLogsModel);
  });

  it('given one series should return expected logs model', () => {
    const series: DataFrame[] = [
      new MutableDataFrame({
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: ['2019-04-26T09:28:11.352440161Z', '2019-04-26T14:42:50.991981292Z'],
          },
          {
            name: 'message',
            type: FieldType.string,
            values: [
              't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
              't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
            ],
            labels: {
              filename: '/var/log/grafana/grafana.log',
              job: 'grafana',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['foo', 'bar'],
          },
        ],
        meta: {
          limit: 1000,
        },
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.hasUniqueLabels).toBeFalsy();
    expect(logsModel.rows).toHaveLength(2);
    expect(logsModel.rows).toMatchObject([
      {
        entry: 't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
        labels: { filename: '/var/log/grafana/grafana.log', job: 'grafana' },
        logLevel: 'info',
        uniqueLabels: {},
        uid: 'foo',
      },
      {
        entry: 't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
        labels: { filename: '/var/log/grafana/grafana.log', job: 'grafana' },
        logLevel: 'error',
        uniqueLabels: {},
        uid: 'bar',
      },
    ]);

    expect(logsModel.series).toHaveLength(2);
    expect(logsModel.meta).toHaveLength(2);
    expect(logsModel.meta[0]).toMatchObject({
      label: 'Common labels',
      value: series[0].fields[1].labels,
      kind: LogsMetaKind.LabelsMap,
    });
    expect(logsModel.meta[1]).toMatchObject({
      label: 'Limit',
      value: `1000 (2 returned)`,
      kind: LogsMetaKind.String,
    });
  });

  it('given one series without labels should return expected logs model', () => {
    const series: DataFrame[] = [
      new MutableDataFrame({
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: ['1970-01-01T00:00:01Z'],
          },
          {
            name: 'message',
            type: FieldType.string,
            values: ['WARN boooo'],
          },
          {
            name: 'level',
            type: FieldType.string,
            values: ['dbug'],
          },
        ],
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.rows).toHaveLength(1);
    expect(logsModel.rows).toMatchObject([
      {
        entry: 'WARN boooo',
        labels: undefined,
        logLevel: LogLevel.debug,
        uniqueLabels: {},
      },
    ]);
  });

  it('given multiple series with unique times should return expected logs model', () => {
    const series: DataFrame[] = [
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:01Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo'],
            labels: {
              foo: 'bar',
              baz: '1',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['0'],
          },
        ],
      }),
      toDataFrame({
        name: 'logs',
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: ['1970-01-01T00:00:00Z', '1970-01-01T00:00:02Z'],
          },
          {
            name: 'message',
            type: FieldType.string,
            values: ['INFO 1', 'INFO 2'],
            labels: {
              foo: 'bar',
              baz: '2',
              level: 'err',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['1', '2'],
          },
        ],
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.hasUniqueLabels).toBeTruthy();
    expect(logsModel.rows).toHaveLength(3);
    expect(logsModel.rows).toMatchObject([
      {
        entry: 'INFO 1',
        labels: { foo: 'bar', baz: '2' },
        logLevel: LogLevel.error,
        uniqueLabels: { baz: '2' },
      },
      {
        entry: 'WARN boooo',
        labels: { foo: 'bar', baz: '1' },
        logLevel: LogLevel.debug,
        uniqueLabels: { baz: '1' },
      },
      {
        entry: 'INFO 2',
        labels: { foo: 'bar', baz: '2' },
        logLevel: LogLevel.error,
        uniqueLabels: { baz: '2' },
      },
    ]);

    expect(logsModel.series).toHaveLength(2);
    expect(logsModel.meta).toHaveLength(1);
    expect(logsModel.meta[0]).toMatchObject({
      label: 'Common labels',
      value: {
        foo: 'bar',
      },
      kind: LogsMetaKind.LabelsMap,
    });
  });
  it('given multiple series with equal times should return expected logs model', () => {
    const series: DataFrame[] = [
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:00Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 1'],
            labels: {
              foo: 'bar',
              baz: '1',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['0'],
          },
        ],
      }),
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:01Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 2'],
            labels: {
              foo: 'bar',
              baz: '2',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['1'],
          },
        ],
      }),
      toDataFrame({
        name: 'logs',
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: ['1970-01-01T00:00:00Z', '1970-01-01T00:00:01Z'],
          },
          {
            name: 'message',
            type: FieldType.string,
            values: ['INFO 1', 'INFO 2'],
            labels: {
              foo: 'bar',
              baz: '2',
              level: 'err',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['2', '3'],
          },
        ],
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.hasUniqueLabels).toBeTruthy();
    expect(logsModel.rows).toHaveLength(4);
    expect(logsModel.rows).toMatchObject([
      {
        entry: 'WARN boooo 1',
        labels: { foo: 'bar', baz: '1' },
        logLevel: LogLevel.debug,
        uniqueLabels: { baz: '1' },
      },
      {
        entry: 'INFO 1',
        labels: { foo: 'bar', baz: '2' },
        logLevel: LogLevel.error,
        uniqueLabels: { baz: '2' },
      },
      {
        entry: 'WARN boooo 2',
        labels: { foo: 'bar', baz: '2' },
        logLevel: LogLevel.debug,
        uniqueLabels: { baz: '2' },
      },
      {
        entry: 'INFO 2',
        labels: { foo: 'bar', baz: '2' },
        logLevel: LogLevel.error,
        uniqueLabels: { baz: '2' },
      },
    ]);
  });

  it('should fallback to row index if no id', () => {
    const series: DataFrame[] = [
      toDataFrame({
        labels: { foo: 'bar' },
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:00Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 1'],
          },
        ],
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.rows[0].uid).toBe('0');
  });

  it('given multiple series with equal ids should return expected logs model', () => {
    const series: DataFrame[] = [
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:00Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 1'],
            labels: {
              foo: 'bar',
              baz: '1',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['0'],
          },
        ],
      }),
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:01Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 2'],
            labels: {
              foo: 'bar',
              baz: '2',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['1'],
          },
        ],
      }),
      toDataFrame({
        fields: [
          {
            name: 'ts',
            type: FieldType.time,
            values: ['1970-01-01T00:00:01Z'],
          },
          {
            name: 'line',
            type: FieldType.string,
            values: ['WARN boooo 2'],
            labels: {
              foo: 'bar',
              baz: '2',
              level: 'dbug',
            },
          },
          {
            name: 'id',
            type: FieldType.string,
            values: ['1'],
          },
        ],
      }),
    ];
    const logsModel = dataFrameToLogsModel(series, 0, 'utc');
    expect(logsModel.hasUniqueLabels).toBeTruthy();
    expect(logsModel.rows).toHaveLength(2);
    expect(logsModel.rows).toMatchObject([
      {
        entry: 'WARN boooo 1',
        labels: { foo: 'bar' },
        logLevel: LogLevel.debug,
        uniqueLabels: { baz: '1' },
      },
      {
        entry: 'WARN boooo 2',
        labels: { foo: 'bar' },
        logLevel: LogLevel.debug,
        uniqueLabels: { baz: '2' },
      },
    ]);
  });
});
Example #23
Source File: fieldOverrides.test.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
describe('FieldOverrides', () => {
  beforeAll(() => {
    standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
  });

  const f0 = new MutableDataFrame();
  f0.add({ title: 'AAA', value: 100, value2: 1234 }, true);
  f0.add({ title: 'BBB', value: -20 }, true);
  f0.add({ title: 'CCC', value: 200, value2: 1000 }, true);
  expect(f0.length).toEqual(3);

  // Hardcode the max value
  f0.fields[1].config.max = 0;
  f0.fields[1].config.decimals = 6;

  const src: FieldConfigSource = {
    defaults: {
      unit: 'xyz',
      decimals: 2,
    },
    overrides: [
      {
        matcher: { id: FieldMatcherID.numeric },
        properties: [
          { prop: 'decimals', value: 1 }, // Numeric
          { prop: 'title', value: 'Kittens' }, // Text
        ],
      },
    ],
  };

  it('will merge FieldConfig with default values', () => {
    const field: FieldConfig = {
      min: 0,
      max: 100,
    };
    const f1 = {
      unit: 'ms',
      dateFormat: '', // should be ignored
      max: parseFloat('NOPE'), // should be ignored
      min: null, // should alo be ignored!
    };

    const f: DataFrame = toDataFrame({
      fields: [{ type: FieldType.number, name: 'x', config: field, values: [] }],
    });
    const processed = applyFieldOverrides({
      data: [f],
      standard: standardFieldConfigEditorRegistry,
      fieldOptions: {
        defaults: f1 as FieldConfig,
        overrides: [],
      },
      replaceVariables: v => v,
      theme: getTheme(),
    })[0];
    const out = processed.fields[0].config;

    expect(out.min).toEqual(0);
    expect(out.max).toEqual(100);
    expect(out.unit).toEqual('ms');
  });

  it('will apply field overrides', () => {
    const data = applyFieldOverrides({
      data: [f0], // the frame
      fieldOptions: src as FieldDisplayOptions, // defaults + overrides
      replaceVariables: (undefined as any) as InterpolateFunction,
      theme: (undefined as any) as GrafanaTheme,
    })[0];
    const valueColumn = data.fields[1];
    const config = valueColumn.config;

    // Keep max from the original setting
    expect(config.max).toEqual(0);

    // Don't Automatically pick the min value
    expect(config.min).toEqual(undefined);

    // The default value applied
    expect(config.unit).toEqual('xyz');

    // The default value applied
    expect(config.title).toEqual('Kittens');

    // The override applied
    expect(config.decimals).toEqual(1);
  });

  it('will apply set min/max when asked', () => {
    const data = applyFieldOverrides({
      data: [f0], // the frame
      fieldOptions: src as FieldDisplayOptions, // defaults + overrides
      replaceVariables: (undefined as any) as InterpolateFunction,
      theme: (undefined as any) as GrafanaTheme,
      autoMinMax: true,
    })[0];
    const valueColumn = data.fields[1];
    const config = valueColumn.config;

    // Keep max from the original setting
    expect(config.max).toEqual(0);

    // Don't Automatically pick the min value
    expect(config.min).toEqual(-20);
  });
});