import { AnyObject, BasicConfig, Config, JsonTree, Operator, Settings, SimpleField, SqlFormatOperator, Utils, ValueSource, Widgets, } from '@react-awesome-query-builder/ui'; import { List } from 'immutable'; import { isString } from 'lodash'; import React from 'react'; import { dateTime, toOption } from '@grafana/data'; import { Button, DateTimePicker, Input, Select } from '@grafana/ui'; const buttonLabels = { add: 'Add', remove: 'Remove', }; export const emptyInitTree: JsonTree = { id: Utils.uuid(), type: 'group', }; const TIME_FILTER = 'timeFilter'; const macros = [TIME_FILTER]; export const widgets: Widgets = { ...BasicConfig.widgets, text: { ...BasicConfig.widgets.text, factory: function TextInput(props) { return ( props?.setValue(e.currentTarget.value)} /> ); }, }, number: { ...BasicConfig.widgets.number, factory: function NumberInput(props) { return ( props?.setValue(Number.parseInt(e.currentTarget.value, 10))} /> ); }, }, datetime: { ...BasicConfig.widgets.datetime, factory: function DateTimeInput(props) { if (props?.operator === Op.MACROS) { return ( conjProps?.setConjunction(val.value!)} /> ); }, renderField: function Field(fieldProps) { const fields = fieldProps?.config?.fields || {}; return ( ({ label: op.label, value: op.key }))} aria-label="Operator" menuShouldPortal value={operatorProps?.selectedKey} onChange={(val) => { operatorProps?.setField(val.value || ''); }} /> ); }, }; // add IN / NOT IN operators to text to support multi-value variables const enum Op { IN = 'select_any_in', NOT_IN = 'select_not_any_in', MACROS = 'macros', } // eslint-ignore const customOperators = getCustomOperators(BasicConfig); const textWidget = BasicConfig.types.text.widgets.text; const opers = [...(textWidget.operators || []), Op.IN, Op.NOT_IN]; const customTextWidget = { ...textWidget, operators: opers, }; const customTypes = { ...BasicConfig.types, text: { ...BasicConfig.types.text, widgets: { ...BasicConfig.types.text.widgets, text: customTextWidget, }, }, datetime: { ...BasicConfig.types.datetime, widgets: { ...BasicConfig.types.datetime.widgets, datetime: { ...BasicConfig.types.datetime.widgets.datetime, operators: [Op.MACROS, ...(BasicConfig.types.datetime.widgets.datetime.operators || [])], }, }, }, }; export const raqbConfig: Config = { ...BasicConfig, widgets, settings, operators: customOperators, types: customTypes, }; export type { Config }; const noop = () => ''; const isSqlFormatOp = (func: unknown): func is SqlFormatOperator => { return typeof func === 'function'; }; function getCustomOperators(config: BasicConfig) { const { ...supportedOperators } = config.operators; // IN operator expects array, override IN formatter for multi-value variables const sqlFormatInOpOrNoop = () => { const sqlFormatOp = supportedOperators[Op.IN].sqlFormatOp; if (isSqlFormatOp(sqlFormatOp)) { return sqlFormatOp; } return noop; }; const customSqlInFormatter = ( field: string, op: string, value: string | List, valueSrc: ValueSource, valueType: string, opDef: Operator, operatorOptions: AnyObject, fieldDef: SimpleField ) => { return sqlFormatInOpOrNoop()( field, op, splitIfString(value), valueSrc, valueType, opDef, operatorOptions, fieldDef ); }; // NOT IN operator expects array, override NOT IN formatter for multi-value variables const sqlFormatNotInOpOrNoop = () => { const sqlFormatOp = supportedOperators[Op.NOT_IN].sqlFormatOp; if (isSqlFormatOp(sqlFormatOp)) { return sqlFormatOp; } return noop; }; const customSqlNotInFormatter = ( field: string, op: string, value: string | List, valueSrc: ValueSource, valueType: string, opDef: Operator, operatorOptions: AnyObject, fieldDef: SimpleField ) => { return sqlFormatNotInOpOrNoop()( field, op, splitIfString(value), valueSrc, valueType, opDef, operatorOptions, fieldDef ); }; const customOperators = { ...supportedOperators, [Op.IN]: { ...supportedOperators[Op.IN], sqlFormatOp: customSqlInFormatter, }, [Op.NOT_IN]: { ...supportedOperators[Op.NOT_IN], sqlFormatOp: customSqlNotInFormatter, }, [Op.MACROS]: { label: 'Macros', sqlFormatOp: (field: string, _operator: string, value: string | List) => { if (value === TIME_FILTER) { return `$__timeFilter(${field})`; } return value; }, }, }; return customOperators; } // value: string | List but AQB uses a different version of Immutable // eslint-ignore function splitIfString(value: any) { if (isString(value)) { return value.split(','); } return value; }