LokiQueryBuilder.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import React, { useMemo } from 'react';
  2. import { DataSourceApi, SelectableValue } from '@grafana/data';
  3. import { EditorRow } from '@grafana/experimental';
  4. import { LabelFilters } from 'app/plugins/datasource/prometheus/querybuilder/shared/LabelFilters';
  5. import { OperationList } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList';
  6. import { OperationsEditorRow } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationsEditorRow';
  7. import { QueryBuilderLabelFilter } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
  8. import { LokiDatasource } from '../../datasource';
  9. import { escapeLabelValueInSelector } from '../../language_utils';
  10. import { lokiQueryModeller } from '../LokiQueryModeller';
  11. import { LokiOperationId, LokiVisualQuery } from '../types';
  12. import { NestedQueryList } from './NestedQueryList';
  13. export interface Props {
  14. query: LokiVisualQuery;
  15. datasource: LokiDatasource;
  16. onChange: (update: LokiVisualQuery) => void;
  17. onRunQuery: () => void;
  18. nested?: boolean;
  19. }
  20. export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, nested, onChange, onRunQuery }) => {
  21. const onChangeLabels = (labels: QueryBuilderLabelFilter[]) => {
  22. onChange({ ...query, labels });
  23. };
  24. const withTemplateVariableOptions = async (optionsPromise: Promise<string[]>): Promise<SelectableValue[]> => {
  25. const options = await optionsPromise;
  26. return [...datasource.getVariables(), ...options].map((value) => ({ label: value, value }));
  27. };
  28. const onGetLabelNames = async (forLabel: Partial<QueryBuilderLabelFilter>): Promise<any> => {
  29. const labelsToConsider = query.labels.filter((x) => x !== forLabel);
  30. if (labelsToConsider.length === 0) {
  31. await datasource.languageProvider.refreshLogLabels();
  32. return datasource.languageProvider.getLabelKeys();
  33. }
  34. const expr = lokiQueryModeller.renderLabels(labelsToConsider);
  35. const series = await datasource.languageProvider.fetchSeriesLabels(expr);
  36. return Object.keys(series).sort();
  37. };
  38. const onGetLabelValues = async (forLabel: Partial<QueryBuilderLabelFilter>) => {
  39. if (!forLabel.label) {
  40. return [];
  41. }
  42. let values;
  43. const labelsToConsider = query.labels.filter((x) => x !== forLabel);
  44. if (labelsToConsider.length === 0) {
  45. values = await datasource.languageProvider.fetchLabelValues(forLabel.label);
  46. } else {
  47. const expr = lokiQueryModeller.renderLabels(labelsToConsider);
  48. const result = await datasource.languageProvider.fetchSeriesLabels(expr);
  49. values = result[datasource.interpolateString(forLabel.label)];
  50. }
  51. return values ? values.map((v) => escapeLabelValueInSelector(v, forLabel.op)) : []; // Escape values in return
  52. };
  53. const labelFilterError: string | undefined = useMemo(() => {
  54. const { labels, operations: op } = query;
  55. if (!labels.length && op.length) {
  56. // We don't want to show error for initial state with empty line contains operation
  57. if (op.length === 1 && op[0].id === LokiOperationId.LineContains && op[0].params[0] === '') {
  58. return undefined;
  59. }
  60. return 'You need to specify at least 1 label filter (stream selector)';
  61. }
  62. return undefined;
  63. }, [query]);
  64. return (
  65. <>
  66. <EditorRow>
  67. <LabelFilters
  68. onGetLabelNames={(forLabel: Partial<QueryBuilderLabelFilter>) =>
  69. withTemplateVariableOptions(onGetLabelNames(forLabel))
  70. }
  71. onGetLabelValues={(forLabel: Partial<QueryBuilderLabelFilter>) =>
  72. withTemplateVariableOptions(onGetLabelValues(forLabel))
  73. }
  74. labelsFilters={query.labels}
  75. onChange={onChangeLabels}
  76. error={labelFilterError}
  77. />
  78. </EditorRow>
  79. <OperationsEditorRow>
  80. <OperationList
  81. queryModeller={lokiQueryModeller}
  82. query={query}
  83. onChange={onChange}
  84. onRunQuery={onRunQuery}
  85. datasource={datasource as DataSourceApi}
  86. />
  87. </OperationsEditorRow>
  88. {query.binaryQueries && query.binaryQueries.length > 0 && (
  89. <NestedQueryList query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />
  90. )}
  91. </>
  92. );
  93. });
  94. LokiQueryBuilder.displayName = 'LokiQueryBuilder';