@grafana/data#ExploreMode TypeScript Examples

The following examples show how to use @grafana/data#ExploreMode. 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.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
DEFAULT_EXPLORE_STATE: ExploreUrlState = {
  datasource: null,
  queries: [],
  range: DEFAULT_RANGE,
  mode: ExploreMode.Metrics,
  ui: {
    showingGraph: true,
    showingTable: true,
    showingLogs: true,
    dedupStrategy: LogsDedupStrategy.none,
  },
  originPanelId: undefined,
}
Example #2
Source File: richHistory.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
createUrlFromRichHistory = (query: RichHistoryQuery) => {
  const queries = query.queries.map(query => ({ expr: query }));
  const exploreState: ExploreUrlState = {
    /* Default range, as we are not saving timerange in rich history */
    range: { from: 'now-1h', to: 'now' },
    datasource: query.datasourceName,
    queries,
    /* Default mode. In the future, we can also save the query mode */
    mode: query.datasourceId === 'loki' ? ExploreMode.Logs : ExploreMode.Metrics,
    ui: {
      showingGraph: true,
      showingLogs: true,
      showingTable: true,
    },
    context: 'explore',
  };

  const serializedState = serializeStateToUrlParam(exploreState, true);
  const baseUrl = /.*(?=\/explore)/.exec(`${window.location.href}`)[0];
  const url = renderUrl(`${baseUrl}/explore`, { left: serializedState });
  return url;
}
Example #3
Source File: LokiExploreQueryEditor.test.tsx    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
describe('LokiExploreQueryEditor', () => {
  let originalGetSelection: typeof window.getSelection;
  beforeAll(() => {
    originalGetSelection = window.getSelection;
    window.getSelection = () => null;
  });

  afterAll(() => {
    window.getSelection = originalGetSelection;
  });

  it('should render component', () => {
    const wrapper = setup(shallow);
    expect(wrapper).toMatchSnapshot();
  });

  it('should render LokiQueryField with ExtraFieldElement when ExploreMode is set to Logs', async () => {
    await act(async () => {
      const wrapper = setup(mount);
      expect(wrapper.find(LokiExploreExtraField).length).toBe(1);
    });
  });

  it('should render LokiQueryField with no ExtraFieldElement when ExploreMode is not Logs', async () => {
    await act(async () => {
      const wrapper = setup(mount, { exploreMode: ExploreMode.Metrics });
      expect(wrapper.find(LokiExploreExtraField).length).toBe(0);
    });
  });
});
Example #4
Source File: ResultProcessor.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
getLogsResult(): LogsModel | null {
    if (this.state.mode !== ExploreMode.Logs) {
      return null;
    }

    const newResults = dataFrameToLogsModel(this.dataFrames, this.intervalMs, this.timeZone);
    const sortOrder = refreshIntervalToSortOrder(this.state.refreshInterval);
    const sortedNewResults = sortLogsResult(newResults, sortOrder);
    const rows = sortedNewResults.rows;
    const series = sortedNewResults.series;
    return { ...sortedNewResults, rows, series };
  }
Example #5
Source File: ResultProcessor.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
getGraphResult(): GraphSeriesXY[] | null {
    if (this.state.mode !== ExploreMode.Metrics) {
      return null;
    }

    const onlyTimeSeries = this.dataFrames.filter(isTimeSeries);

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

    return getGraphSeriesModel(
      onlyTimeSeries,
      this.timeZone,
      {},
      { showBars: false, showLines: true, showPoints: false },
      { asTable: false, isVisible: true, placement: 'under' }
    );
  }
Example #6
Source File: reducers.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
getModesForDatasource = (dataSource: DataSourceApi, currentMode: ExploreMode): [ExploreMode[], ExploreMode] => {
  const supportsGraph = dataSource.meta.metrics;
  const supportsLogs = dataSource.meta.logs;

  let mode = currentMode || ExploreMode.Metrics;
  const supportedModes: ExploreMode[] = [];

  if (supportsGraph) {
    supportedModes.push(ExploreMode.Metrics);
  }

  if (supportsLogs) {
    supportedModes.push(ExploreMode.Logs);
  }

  if (supportedModes.length === 1) {
    mode = supportedModes[0];
  }

  // HACK: Used to set Loki's default explore mode to Logs mode.
  // A better solution would be to introduce a "default" or "preferred" mode to the datasource config
  if (dataSource.meta.name === 'Loki' && !currentMode) {
    mode = ExploreMode.Logs;
  }

  return [supportedModes, mode];
}
Example #7
Source File: actions.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
/**
 * Initialize Explore state with state from the URL and the React component.
 * Call this only on components for with the Explore state has not been initialized.
 */
export function initializeExplore(
  exploreId: ExploreId,
  datasourceName: string,
  queries: DataQuery[],
  range: TimeRange,
  mode: ExploreMode,
  containerWidth: number,
  eventBridge: Emitter,
  ui: ExploreUIState,
  originPanelId: number
): ThunkResult<void> {
  return async (dispatch, getState) => {
    dispatch(loadExploreDatasourcesAndSetDatasource(exploreId, datasourceName));
    dispatch(
      initializeExploreAction({
        exploreId,
        containerWidth,
        eventBridge,
        queries,
        range,
        mode,
        ui,
        originPanelId,
      })
    );
    dispatch(updateTime({ exploreId }));
    const richHistory = getRichHistory();
    dispatch(richHistoryUpdatedAction({ richHistory }));
  };
}
Example #8
Source File: reducers.test.ts    From grafana-chinese with Apache License 2.0 6 votes vote down vote up
setup = (urlStateOverrides?: any) => {
  const update = makeInitialUpdateState();
  const urlStateDefaults: ExploreUrlState = {
    datasource: 'some-datasource',
    queries: [],
    range: {
      from: '',
      to: '',
    },
    mode: ExploreMode.Metrics,
    ui: {
      dedupStrategy: LogsDedupStrategy.none,
      showingGraph: false,
      showingTable: false,
      showingLogs: false,
    },
  };
  const urlState: ExploreUrlState = { ...urlStateDefaults, ...urlStateOverrides };
  const serializedUrlState = serializeStateToUrlParam(urlState);
  const initialState = ({
    split: false,
    left: { urlState, update },
    right: { urlState, update },
  } as unknown) as ExploreState;

  return {
    initialState,
    serializedUrlState,
  };
}
Example #9
Source File: explore.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
export function parseUrlState(initial: string | undefined): ExploreUrlState {
  const parsed = safeParseJson(initial);
  const errorResult: any = {
    datasource: null,
    queries: [],
    range: DEFAULT_RANGE,
    ui: DEFAULT_UI_STATE,
    mode: null,
    originPanelId: null,
  };

  if (!parsed) {
    return errorResult;
  }

  if (!Array.isArray(parsed)) {
    return parsed;
  }

  if (parsed.length <= ParseUrlStateIndex.SegmentsStart) {
    console.error('Error parsing compact URL state for Explore.');
    return errorResult;
  }

  const range = {
    from: parsed[ParseUrlStateIndex.RangeFrom],
    to: parsed[ParseUrlStateIndex.RangeTo],
  };
  const datasource = parsed[ParseUrlStateIndex.Datasource];
  const parsedSegments = parsed.slice(ParseUrlStateIndex.SegmentsStart);
  const metricProperties = ['expr', 'target', 'datasource', 'query'];
  const queries = parsedSegments.filter(segment => isSegment(segment, ...metricProperties));
  const modeObj = parsedSegments.filter(segment => isSegment(segment, 'mode'))[0];
  const mode = modeObj ? modeObj.mode : ExploreMode.Metrics;

  const uiState = parsedSegments.filter(segment => isSegment(segment, 'ui'))[0];
  const ui = uiState
    ? {
        showingGraph: uiState.ui[ParseUiStateIndex.Graph],
        showingLogs: uiState.ui[ParseUiStateIndex.Logs],
        showingTable: uiState.ui[ParseUiStateIndex.Table],
        dedupStrategy: uiState.ui[ParseUiStateIndex.Strategy],
      }
    : DEFAULT_UI_STATE;

  const originPanelId = parsedSegments.filter(segment => isSegment(segment, 'originPanelId'))[0];
  return { datasource, queries, range, ui, mode, originPanelId };
}
Example #10
Source File: datasource.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
query(options: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> {
    const subQueries: Array<Observable<DataQueryResponse>> = [];
    const filteredTargets = options.targets
      .filter(target => target.expr && !target.hide)
      .map(target => ({
        ...target,
        expr: this.templateSrv.replace(target.expr, options.scopedVars, this.interpolateQueryExpr),
      }));

    if (options.exploreMode === ExploreMode.Metrics) {
      filteredTargets.forEach(target =>
        subQueries.push(
          this.runInstantQuery(target, options, filteredTargets.length),
          this.runRangeQueryWithFallback(target, options, filteredTargets.length)
        )
      );
    } else {
      filteredTargets.forEach(target =>
        subQueries.push(
          this.runRangeQueryWithFallback(target, options, filteredTargets.length).pipe(
            map(dataQueryResponse => {
              if (options.exploreMode === ExploreMode.Logs && dataQueryResponse.data.find(d => isTimeSeries(d))) {
                throw new Error(
                  'Logs mode does not support queries that return time series data. Please perform a logs query or switch to Metrics mode.'
                );
              } else {
                return dataQueryResponse;
              }
            })
          )
        )
      );
    }

    // No valid targets, return the empty result to save a round trip.
    if (isEmpty(subQueries)) {
      return of({
        data: [],
        state: LoadingState.Done,
      });
    }

    return merge(...subQueries);
  }
Example #11
Source File: LokiExploreQueryEditor.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
export function LokiExploreQueryEditor(props: Props) {
  const { query, data, datasource, exploreMode, history, onChange, onRunQuery } = props;

  let absolute: AbsoluteTimeRange;
  if (data && !_.isEmpty(data.request)) {
    const { range } = data.request;

    absolute = {
      from: range.from.valueOf(),
      to: range.to.valueOf(),
    };
  } else {
    absolute = {
      from: Date.now() - 10000,
      to: Date.now(),
    };
  }

  function onChangeQueryLimit(value: string) {
    const { query, onChange } = props;
    const nextQuery = { ...query, maxLines: preprocessMaxLines(value) };
    onChange(nextQuery);
  }

  function preprocessMaxLines(value: string): number {
    if (value.length === 0) {
      // empty input - falls back to dataSource.maxLines limit
      return NaN;
    } else if (value.length > 0 && (isNaN(+value) || +value < 0)) {
      // input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative
      // falls back to the limit of 0 lines
      return 0;
    } else {
      // default case - correct input
      return +value;
    }
  }

  function onMaxLinesChange(e: React.SyntheticEvent<HTMLInputElement>) {
    if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) {
      onChangeQueryLimit(e.currentTarget.value);
    }
  }

  function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
    if (e.key === 'Enter') {
      onRunQuery();
    }
  }

  return (
    <LokiQueryField
      datasource={datasource}
      query={query}
      onChange={onChange}
      onRunQuery={onRunQuery}
      history={history}
      data={data}
      absoluteRange={absolute}
      ExtraFieldElement={
        exploreMode === ExploreMode.Logs ? (
          <LokiExploreExtraField
            label={'Line limit'}
            onChangeFunc={onMaxLinesChange}
            onKeyDownFunc={onReturnKeyDown}
            value={query?.maxLines?.toString() || ''}
            type={'number'}
            min={0}
          />
        ) : null
      }
    />
  );
}
Example #12
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 #13
Source File: LokiCheatSheet.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
render() {
    const { exploreMode } = this.props;

    return exploreMode === ExploreMode.Logs ? this.renderLogsCheatSheet() : this.renderMetricsCheatSheet();
  }
Example #14
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 #15
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 #16
Source File: reducers.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
updateChildRefreshState = (
  state: Readonly<ExploreItemState>,
  payload: LocationUpdate,
  exploreId: ExploreId
): ExploreItemState => {
  const path = payload.path || '';
  const queryState = payload.query[exploreId] as string;
  if (!queryState) {
    return state;
  }

  const urlState = parseUrlState(queryState);
  if (!state.urlState || path !== '/explore') {
    // we only want to refresh when browser back/forward
    return {
      ...state,
      urlState,
      update: { datasource: false, queries: false, range: false, mode: false, ui: false },
    };
  }

  const datasource = _.isEqual(urlState ? urlState.datasource : '', state.urlState.datasource) === false;
  const queries = _.isEqual(urlState ? urlState.queries : [], state.urlState.queries) === false;
  const range = _.isEqual(urlState ? urlState.range : DEFAULT_RANGE, state.urlState.range) === false;
  const mode = _.isEqual(urlState ? urlState.mode : ExploreMode.Metrics, state.urlState.mode) === false;
  const ui = _.isEqual(urlState ? urlState.ui : DEFAULT_UI_STATE, state.urlState.ui) === false;

  return {
    ...state,
    urlState,
    update: {
      ...state.update,
      datasource,
      queries,
      range,
      mode,
      ui,
    },
  };
}
Example #17
Source File: actions.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
/**
 * Change the display mode in Explore.
 */
export function changeMode(exploreId: ExploreId, mode: ExploreMode): ThunkResult<void> {
  return dispatch => {
    dispatch(changeModeAction({ exploreId, mode }));
  };
}
Example #18
Source File: actions.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
/**
 * Loads a new datasource identified by the given name.
 */
export function changeDatasource(exploreId: ExploreId, datasource: string): ThunkResult<void> {
  return async (dispatch, getState) => {
    let newDataSourceInstance: DataSourceApi = null;

    if (!datasource) {
      newDataSourceInstance = await getDatasourceSrv().get();
    } else {
      newDataSourceInstance = await getDatasourceSrv().get(datasource);
    }

    const currentDataSourceInstance = getState().explore[exploreId].datasourceInstance;
    const queries = getState().explore[exploreId].queries;
    const orgId = getState().user.orgId;
    const datasourceVersion = newDataSourceInstance.getVersion && (await newDataSourceInstance.getVersion());

    // HACK: Switch to logs mode if coming from Prometheus to Loki
    const prometheusToLoki =
      currentDataSourceInstance?.meta?.name === 'Prometheus' && newDataSourceInstance?.meta?.name === 'Loki';

    dispatch(
      updateDatasourceInstanceAction({
        exploreId,
        datasourceInstance: newDataSourceInstance,
        version: datasourceVersion,
        mode: prometheusToLoki ? ExploreMode.Logs : undefined,
      })
    );

    await dispatch(importQueries(exploreId, queries, currentDataSourceInstance, newDataSourceInstance));

    if (getState().explore[exploreId].isLive) {
      dispatch(changeRefreshInterval(exploreId, RefreshPicker.offOption.value));
    }

    await dispatch(loadDatasource(exploreId, newDataSourceInstance, orgId));
    dispatch(runQueries(exploreId));
  };
}
Example #19
Source File: actions.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
describe('changing datasource', () => {
  it('should switch to logs mode when changing from prometheus to loki', async () => {
    const lokiMock = {
      testDatasource: () => Promise.resolve({ status: 'success' }),
      name: 'Loki',
      init: jest.fn(),
      meta: { id: 'some id', name: 'Loki' },
    };

    getDatasourceSrvMock.mockImplementation(
      () =>
        ({
          getExternal: jest.fn().mockReturnValue([]),
          get: jest.fn().mockReturnValue(lokiMock),
        } as any)
    );

    const exploreId = ExploreId.left;
    const name = 'Prometheus';
    const mockPromDatasourceInstance = {
      testDatasource: () => Promise.resolve({ status: 'success' }),
      name,
      init: jest.fn(),
      meta: { id: 'some id', name },
    };

    const initialState = {
      explore: {
        [exploreId]: {
          requestedDatasourceName: 'Loki',
          datasourceInstance: mockPromDatasourceInstance,
        },
      },
      user: {
        orgId: 1,
      },
    };

    jest.spyOn(Actions, 'importQueries').mockImplementationOnce(() => jest.fn);
    jest.spyOn(Actions, 'loadDatasource').mockImplementationOnce(() => jest.fn);
    jest.spyOn(Actions, 'runQueries').mockImplementationOnce(() => jest.fn);
    const dispatchedActions = await thunkTester(initialState)
      .givenThunk(changeDatasource)
      .whenThunkIsDispatched(exploreId, name);

    expect(dispatchedActions).toEqual([
      updateDatasourceInstanceAction({
        exploreId,
        datasourceInstance: lokiMock as any,
        version: undefined,
        mode: ExploreMode.Logs,
      }),
    ]);
  });
});
Example #20
Source File: actions.test.ts    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
setup = (updateOverides?: Partial<ExploreUpdateState>) => {
  const exploreId = ExploreId.left;
  const containerWidth = 1920;
  const eventBridge = {} as Emitter;
  const ui = { dedupStrategy: LogsDedupStrategy.none, showingGraph: false, showingLogs: false, showingTable: false };
  const timeZone = DefaultTimeZone;
  const range = testRange;
  const urlState: ExploreUrlState = {
    datasource: 'some-datasource',
    queries: [],
    range: range.raw,
    mode: ExploreMode.Metrics,
    ui,
  };
  const updateDefaults = makeInitialUpdateState();
  const update = { ...updateDefaults, ...updateOverides };
  const initialState = {
    user: {
      orgId: '1',
      timeZone,
    },
    explore: {
      [exploreId]: {
        initialized: true,
        urlState,
        containerWidth,
        eventBridge,
        update,
        datasourceInstance: { name: 'some-datasource' },
        queries: [] as DataQuery[],
        range,
        ui,
        refreshInterval: {
          label: 'Off',
          value: 0,
        },
      },
    },
  };

  return {
    initialState,
    exploreId,
    range,
    ui,
    containerWidth,
    eventBridge,
  };
}
Example #21
Source File: QueryRow.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
render() {
    const {
      datasourceInstance,
      history,
      query,
      exploreEvents,
      range,
      absoluteRange,
      queryResponse,
      mode,
      latency,
    } = this.props;

    const canToggleEditorModes =
      mode === ExploreMode.Metrics && has(datasourceInstance, 'components.QueryCtrl.prototype.toggleEditorMode');
    const isNotStarted = queryResponse.state === LoadingState.NotStarted;
    const queryErrors = queryResponse.error && queryResponse.error.refId === query.refId ? [queryResponse.error] : [];
    let QueryField;

    if (mode === ExploreMode.Metrics && datasourceInstance.components?.ExploreMetricsQueryField) {
      QueryField = datasourceInstance.components.ExploreMetricsQueryField;
    } else if (mode === ExploreMode.Logs && datasourceInstance.components?.ExploreLogsQueryField) {
      QueryField = datasourceInstance.components.ExploreLogsQueryField;
    } else {
      QueryField = datasourceInstance.components?.ExploreQueryField;
    }

    return (
      <>
        <div className="query-row">
          <div className="query-row-field flex-shrink-1">
            {QueryField ? (
              <QueryField
                datasource={datasourceInstance}
                query={query}
                history={history}
                onRunQuery={this.onRunQuery}
                onBlur={noopOnBlur}
                onChange={this.onChange}
                data={queryResponse}
                absoluteRange={absoluteRange}
                exploreMode={mode}
              />
            ) : (
              <QueryEditor
                error={queryErrors}
                datasource={datasourceInstance}
                onQueryChange={this.onChange}
                onExecuteQuery={this.onRunQuery}
                initialQuery={query}
                exploreEvents={exploreEvents}
                range={range}
                textEditModeEnabled={this.state.textEditModeEnabled}
              />
            )}
          </div>
          <QueryRowActions
            canToggleEditorModes={canToggleEditorModes}
            isDisabled={query.hide}
            isNotStarted={isNotStarted}
            latency={latency}
            onClickToggleEditorMode={this.onClickToggleEditorMode}
            onClickToggleDisabled={this.onClickToggleDisabled}
            onClickRemoveButton={this.onClickRemoveButton}
          />
        </div>
        {queryErrors.length > 0 && <ErrorContainer queryError={queryErrors[0]} />}
      </>
    );
  }
Example #22
Source File: ExploreToolbar.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
mapStateToProps = (state: StoreState, { exploreId }: OwnProps): StateProps => {
  const splitted = state.explore.split;
  const syncedTimes = state.explore.syncedTimes;
  const exploreItem: ExploreItemState = state.explore[exploreId];
  const {
    datasourceInstance,
    datasourceMissing,
    range,
    refreshInterval,
    loading,
    supportedModes,
    mode,
    isLive,
    isPaused,
    originPanelId,
    queries,
    datasourceLoading,
    containerWidth,
  } = exploreItem;

  const hasLiveOption = datasourceInstance?.meta?.streaming && mode === ExploreMode.Logs;

  return {
    datasourceMissing,
    datasourceName: datasourceInstance?.name,
    loading,
    range,
    timeZone: getTimeZone(state.user),
    splitted,
    refreshInterval,
    supportedModes,
    selectedMode: supportedModes.includes(mode) ? mode : supportedModes[0],
    hasLiveOption,
    isLive,
    isPaused,
    originPanelId,
    queries,
    syncedTimes,
    datasourceLoading,
    containerWidth,
  };
}
Example #23
Source File: ExploreToolbar.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
onModeChange = (mode: ExploreMode) => {
    const { changeMode, exploreId } = this.props;
    changeMode(exploreId, mode);
  };
Example #24
Source File: Explore.tsx    From grafana-chinese with Apache License 2.0 5 votes vote down vote up
function mapStateToProps(state: StoreState, { exploreId }: ExploreProps): Partial<ExploreProps> {
  const explore = state.explore;
  const { split, syncedTimes } = explore;
  const item: ExploreItemState = explore[exploreId];
  const timeZone = getTimeZone(state.user);
  const {
    datasourceInstance,
    datasourceMissing,
    initialized,
    queryKeys,
    urlState,
    update,
    isLive,
    supportedModes,
    mode,
    graphResult,
    loading,
    showingGraph,
    showingTable,
    absoluteRange,
    queryResponse,
  } = item;

  const { datasource, queries, range: urlRange, mode: urlMode, ui, originPanelId } = (urlState ||
    {}) as ExploreUrlState;
  const initialDatasource = datasource || store.get(lastUsedDatasourceKeyForOrgId(state.user.orgId));
  const initialQueries: DataQuery[] = ensureQueriesMemoized(queries);
  const initialRange = urlRange
    ? getTimeRangeFromUrlMemoized(urlRange, timeZone)
    : getTimeRange(timeZone, DEFAULT_RANGE);

  let newMode: ExploreMode | undefined;

  if (supportedModes.length) {
    const urlModeIsValid = supportedModes.includes(urlMode);
    const modeStateIsValid = supportedModes.includes(mode);

    if (modeStateIsValid) {
      newMode = mode;
    } else if (urlModeIsValid) {
      newMode = urlMode;
    } else {
      newMode = supportedModes[0];
    }
  } else {
    newMode = [ExploreMode.Metrics, ExploreMode.Logs].includes(urlMode) ? urlMode : undefined;
  }

  const initialUI = ui || DEFAULT_UI_STATE;

  return {
    datasourceInstance,
    datasourceMissing,
    initialized,
    split,
    queryKeys,
    update,
    initialDatasource,
    initialQueries,
    initialRange,
    mode: newMode,
    initialUI,
    isLive,
    graphResult,
    loading,
    showingGraph,
    showingTable,
    absoluteRange,
    queryResponse,
    originPanelId,
    syncedTimes,
    timeZone,
  };
}
Example #25
Source File: actions.ts    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
runQueries = (exploreId: ExploreId): ThunkResult<void> => {
  return (dispatch, getState) => {
    dispatch(updateTime({ exploreId }));

    const richHistory = getState().explore.richHistory;
    const exploreItemState = getState().explore[exploreId];
    const {
      datasourceInstance,
      queries,
      containerWidth,
      isLive: live,
      range,
      scanning,
      queryResponse,
      querySubscription,
      history,
      mode,
      showingGraph,
      showingTable,
    } = exploreItemState;

    if (!hasNonEmptyQuery(queries)) {
      dispatch(clearQueriesAction({ exploreId }));
      dispatch(stateSave()); // Remember to save to state and update location
      return;
    }

    // Some datasource's query builders allow per-query interval limits,
    // but we're using the datasource interval limit for now
    const minInterval = datasourceInstance.interval;

    stopQueryState(querySubscription);

    const datasourceId = datasourceInstance.meta.id;

    const queryOptions: QueryOptions = {
      minInterval,
      // maxDataPoints is used in:
      // Loki - used for logs streaming for buffer size, with undefined it falls back to datasource config if it supports that.
      // Elastic - limits the number of datapoints for the counts query and for logs it has hardcoded limit.
      // Influx - used to correctly display logs in graph
      maxDataPoints: mode === ExploreMode.Logs && datasourceId === 'loki' ? undefined : containerWidth,
      liveStreaming: live,
      showingGraph,
      showingTable,
      mode,
    };

    const datasourceName = exploreItemState.requestedDatasourceName;

    const transaction = buildQueryTransaction(queries, queryOptions, range, scanning);

    let firstResponse = true;

    const newQuerySub = runRequest(datasourceInstance, transaction.request)
      .pipe(
        // Simple throttle for live tailing, in case of > 1000 rows per interval we spend about 200ms on processing and
        // rendering. In case this is optimized this can be tweaked, but also it should be only as fast as user
        // actually can see what is happening.
        live ? throttleTime(500) : identity,
        map((data: PanelData) => preProcessPanelData(data, queryResponse))
      )
      .subscribe((data: PanelData) => {
        if (!data.error && firstResponse) {
          // Side-effect: Saving history in localstorage
          const nextHistory = updateHistory(history, datasourceId, queries);
          const arrayOfStringifiedQueries = queries.map(query =>
            datasourceInstance?.getQueryDisplayText
              ? datasourceInstance.getQueryDisplayText(query)
              : getQueryDisplayText(query)
          );

          const nextRichHistory = addToRichHistory(
            richHistory || [],
            datasourceId,
            datasourceName,
            arrayOfStringifiedQueries,
            false,
            '',
            ''
          );
          dispatch(historyUpdatedAction({ exploreId, history: nextHistory }));
          dispatch(richHistoryUpdatedAction({ richHistory: nextRichHistory }));

          // We save queries to the URL here so that only successfully run queries change the URL.
          dispatch(stateSave());
        }

        firstResponse = false;

        dispatch(queryStreamUpdatedAction({ exploreId, response: data }));

        // Keep scanning for results if this was the last scanning transaction
        if (getState().explore[exploreId].scanning) {
          if (data.state === LoadingState.Done && data.series.length === 0) {
            const range = getShiftedTimeRange(-1, getState().explore[exploreId].range);
            dispatch(updateTime({ exploreId, absoluteRange: range }));
            dispatch(runQueries(exploreId));
          } else {
            // We can stop scanning if we have a result
            dispatch(scanStopAction({ exploreId }));
          }
        }
      });

    dispatch(queryStoreSubscriptionAction({ exploreId, querySubscription: newQuerySub }));
  };
}
Example #26
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 #27
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 #28
Source File: ExploreToolbar.tsx    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
render() {
    const {
      datasourceMissing,
      closeSplit,
      exploreId,
      loading,
      range,
      timeZone,
      splitted,
      syncedTimes,
      refreshInterval,
      onChangeTime,
      split,
      supportedModes,
      selectedMode,
      hasLiveOption,
      isLive,
      isPaused,
      originPanelId,
      datasourceLoading,
      containerWidth,
    } = this.props;

    const styles = getStyles();
    const originDashboardIsEditable = originPanelId && Number.isInteger(originPanelId);
    const panelReturnClasses = classNames('btn', 'navbar-button', {
      'btn--radius-right-0': originDashboardIsEditable,
      'navbar-button navbar-button--border-right-0': originDashboardIsEditable,
    });

    const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false;
    const showSmallTimePicker = splitted || containerWidth < 1210;

    return (
      <div className={splitted ? 'explore-toolbar splitted' : 'explore-toolbar'}>
        <div className="explore-toolbar-item">
          <div className="explore-toolbar-header">
            <div className="explore-toolbar-header-title">
              {exploreId === 'left' && (
                <span className="navbar-page-btn">
                  <i className="gicon gicon-explore" />
                  Explore
                </span>
              )}
            </div>
            {splitted && (
              <a className="explore-toolbar-header-close" onClick={() => closeSplit(exploreId)}>
                <i className="fa fa-times fa-fw" />
              </a>
            )}
          </div>
        </div>
        <div className="explore-toolbar-item">
          <div className="explore-toolbar-content">
            {!datasourceMissing ? (
              <div className="explore-toolbar-content-item">
                <div
                  className={classNames(
                    'explore-ds-picker',
                    showSmallDataSourcePicker ? 'explore-ds-picker--small' : ''
                  )}
                >
                  <DataSourcePicker
                    onChange={this.onChangeDatasource}
                    datasources={getExploreDatasources()}
                    current={this.getSelectedDatasource()}
                    showLoading={datasourceLoading}
                    hideTextValue={showSmallDataSourcePicker}
                  />
                </div>
                {supportedModes.length > 1 ? (
                  <div className="query-type-toggle">
                    <ToggleButtonGroup label="" transparent={true}>
                      <ToggleButton
                        key={ExploreMode.Metrics}
                        value={ExploreMode.Metrics}
                        onChange={this.onModeChange}
                        selected={selectedMode === ExploreMode.Metrics}
                      >
                        {'Metrics'}
                      </ToggleButton>
                      <ToggleButton
                        key={ExploreMode.Logs}
                        value={ExploreMode.Logs}
                        onChange={this.onModeChange}
                        selected={selectedMode === ExploreMode.Logs}
                      >
                        {'Logs'}
                      </ToggleButton>
                    </ToggleButtonGroup>
                  </div>
                ) : null}
              </div>
            ) : null}

            {originPanelId && Number.isInteger(originPanelId) && !splitted && (
              <div className="explore-toolbar-content-item">
                <Tooltip content={'Return to panel'} placement="bottom">
                  <button className={panelReturnClasses} onClick={() => this.returnToPanel()}>
                    <i className="fa fa-arrow-left" />
                  </button>
                </Tooltip>
                {originDashboardIsEditable && (
                  <ButtonSelect
                    className="navbar-button--attached btn--radius-left-0$"
                    options={[{ label: 'Return to panel with changes', value: '' }]}
                    onChange={() => this.returnToPanel({ withChanges: true })}
                    maxMenuHeight={380}
                  />
                )}
              </div>
            )}

            {exploreId === 'left' && !splitted ? (
              <div className="explore-toolbar-content-item explore-icon-align">
                <ResponsiveButton
                  splitted={splitted}
                  title="Split"
                  onClick={split}
                  iconClassName="fa fa-fw fa-columns icon-margin-right"
                  disabled={isLive}
                />
              </div>
            ) : null}
            {!isLive && (
              <div className="explore-toolbar-content-item">
                <ExploreTimeControls
                  exploreId={exploreId}
                  range={range}
                  timeZone={timeZone}
                  onChangeTime={onChangeTime}
                  splitted={splitted}
                  syncedTimes={syncedTimes}
                  onChangeTimeSync={this.onChangeTimeSync}
                  hideText={showSmallTimePicker}
                />
              </div>
            )}

            {!isLive && (
              <div className="explore-toolbar-content-item explore-icon-align">
                <ResponsiveButton
                  splitted={splitted}
                  title="Clear All"
                  onClick={this.onClearAll}
                  iconClassName="fa fa-fw fa-trash icon-margin-right"
                />
              </div>
            )}
            <div className="explore-toolbar-content-item">
              <RunButton
                refreshInterval={refreshInterval}
                onChangeRefreshInterval={this.onChangeRefreshInterval}
                splitted={splitted}
                loading={loading || (isLive && !isPaused)}
                onRun={this.onRunQuery}
                showDropdown={!isLive}
              />
              {refreshInterval && <SetInterval func={this.onRunQuery} interval={refreshInterval} loading={loading} />}
            </div>

            {hasLiveOption && (
              <div className={`explore-toolbar-content-item ${styles.liveTailButtons}`}>
                <LiveTailControls exploreId={exploreId}>
                  {controls => (
                    <LiveTailButton
                      splitted={splitted}
                      isLive={isLive}
                      isPaused={isPaused}
                      start={controls.start}
                      pause={controls.pause}
                      resume={controls.resume}
                      stop={controls.stop}
                    />
                  )}
                </LiveTailControls>
              </div>
            )}
          </div>
        </div>
      </div>
    );
  }
Example #29
Source File: Explore.tsx    From grafana-chinese with Apache License 2.0 4 votes vote down vote up
render() {
    const {
      datasourceInstance,
      datasourceMissing,
      exploreId,
      split,
      queryKeys,
      mode,
      graphResult,
      loading,
      absoluteRange,
      showingGraph,
      showingTable,
      timeZone,
      queryResponse,
      syncedTimes,
      isLive,
    } = this.props;
    const { showRichHistory } = this.state;
    const exploreClass = split ? 'explore explore-split' : 'explore';
    const styles = getStyles();
    const StartPage = datasourceInstance?.components?.ExploreStartPage;
    const showStartPage = !queryResponse || queryResponse.state === LoadingState.NotStarted;

    // gets an error without a refID, so non-query-row-related error, like a connection error
    const queryErrors = queryResponse.error ? [queryResponse.error] : undefined;
    const queryError = getFirstNonQueryRowSpecificError(queryErrors);

    return (
      <div className={exploreClass} ref={this.getRef}>
        <ExploreToolbar exploreId={exploreId} onChangeTime={this.onChangeTime} />
        {datasourceMissing ? this.renderEmptyState() : null}
        {datasourceInstance && (
          <div className="explore-container">
            <QueryRows exploreEvents={this.exploreEvents} exploreId={exploreId} queryKeys={queryKeys} />
            <div className="gf-form">
              <button
                aria-label="Add row button"
                className={`gf-form-label gf-form-label--btn ${styles.button}`}
                onClick={this.onClickAddQueryRowButton}
                disabled={isLive}
              >
                <i className={'fa fa-fw fa-plus icon-margin-right'} />
                <span className="btn-title">{'\xA0' + 'Add query'}</span>
              </button>
              <button
                aria-label="Rich history button"
                className={cx(`gf-form-label gf-form-label--btn ${styles.button}`, {
                  ['explore-active-button']: showRichHistory,
                })}
                onClick={this.toggleShowRichHistory}
              >
                <i className={'fa fa-fw fa-history icon-margin-right '} />
                <span className="btn-title">{'\xA0' + 'Query history'}</span>
              </button>
            </div>
            <ErrorContainer queryError={queryError} />
            <AutoSizer onResize={this.onResize} disableHeight>
              {({ width }) => {
                if (width === 0) {
                  return null;
                }

                return (
                  <main className={`m-t-2 ${styles.logsMain}`} style={{ width }}>
                    <ErrorBoundaryAlert>
                      {showStartPage && StartPage && (
                        <div className={'grafana-info-box grafana-info-box--max-lg'}>
                          <StartPage
                            onClickExample={this.onClickExample}
                            datasource={datasourceInstance}
                            exploreMode={mode}
                          />
                        </div>
                      )}
                      {!showStartPage && (
                        <>
                          {mode === ExploreMode.Metrics && (
                            <ExploreGraphPanel
                              series={graphResult}
                              width={width}
                              loading={loading}
                              absoluteRange={absoluteRange}
                              isStacked={false}
                              showPanel={true}
                              showingGraph={showingGraph}
                              showingTable={showingTable}
                              timeZone={timeZone}
                              onToggleGraph={this.onToggleGraph}
                              onUpdateTimeRange={this.onUpdateTimeRange}
                              showBars={false}
                              showLines={true}
                            />
                          )}
                          {mode === ExploreMode.Metrics && (
                            <TableContainer width={width} exploreId={exploreId} onClickCell={this.onClickFilterLabel} />
                          )}
                          {mode === ExploreMode.Logs && (
                            <LogsContainer
                              width={width}
                              exploreId={exploreId}
                              syncedTimes={syncedTimes}
                              onClickFilterLabel={this.onClickFilterLabel}
                              onClickFilterOutLabel={this.onClickFilterOutLabel}
                              onStartScanning={this.onStartScanning}
                              onStopScanning={this.onStopScanning}
                            />
                          )}
                        </>
                      )}
                      {showRichHistory && (
                        <RichHistoryContainer
                          width={width}
                          exploreId={exploreId}
                          onClose={this.toggleShowRichHistory}
                        />
                      )}
                    </ErrorBoundaryAlert>
                  </main>
                );
              }}
            </AutoSizer>
          </div>
        )}
      </div>
    );
  }