Skip to content

Commit 1b06e17

Browse files
authored
Merge pull request #115 from quickwit-oss/ddelemeny/configurable-editor-defaults
Make log limits configurable
2 parents 6bafb31 + 3e51682 commit 1b06e17

File tree

10 files changed

+139
-12
lines changed

10 files changed

+139
-12
lines changed

src/components/QueryEditor/ElasticsearchQueryContext.tsx

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createContext, PropsWithChildren, useCallback, useEffect, useState } from 'react';
1+
import React, { createContext, PropsWithChildren, useCallback, useEffect, useState, FunctionComponent } from 'react';
22

33
import { CoreApp, TimeRange } from '@grafana/data';
44

@@ -10,6 +10,9 @@ import { createReducer as createBucketAggsReducer } from './BucketAggregationsEd
1010
import { reducer as metricsReducer } from './MetricAggregationsEditor/state/reducer';
1111
import { aliasPatternReducer, queryReducer, initQuery, initExploreQuery } from './state';
1212
import { getHook } from '@/utils/context';
13+
import { Provider, useDispatch } from "react-redux";
14+
import { initDefaults } from '@/store/defaults';
15+
import { store } from "@/store"
1316

1417
export const RangeContext = createContext<TimeRange | undefined>(undefined);
1518
export const useRange = getHook(RangeContext);
@@ -29,15 +32,31 @@ interface Props {
2932
range: TimeRange;
3033
}
3134

32-
export const ElasticsearchProvider = ({
35+
function withStore<P extends PropsWithChildren<Props>>(Component: FunctionComponent<P>): FunctionComponent<P>{
36+
const newComp = (props: P) => (
37+
<Provider store={store}>
38+
<Component {...props}/>
39+
</Provider>
40+
)
41+
newComp.displayName = Component.displayName
42+
return newComp
43+
}
44+
45+
export const ElasticsearchProvider = withStore(({
3346
children,
3447
onChange,
3548
onRunQuery,
3649
query,
3750
app,
3851
datasource,
3952
range,
40-
}: PropsWithChildren<Props>) => {
53+
}: PropsWithChildren<Props>): JSX.Element => {
54+
55+
const storeDispatch = useDispatch();
56+
useEffect(()=>{
57+
storeDispatch(initDefaults(datasource.queryEditorConfig?.defaults))
58+
}, [storeDispatch, datasource])
59+
4160
const onStateChange = useCallback(
4261
(query: ElasticsearchQuery) => {
4362
onChange(query);
@@ -77,7 +96,7 @@ export const ElasticsearchProvider = ({
7796
}, [shouldRunInit, dispatch, isUninitialized, app]);
7897

7998
if (isUninitialized) {
80-
return null;
99+
return (<></>);
81100
}
82101

83102
return (
@@ -89,4 +108,4 @@ export const ElasticsearchProvider = ({
89108
</QueryContext.Provider>
90109
</DatasourceContext.Provider>
91110
);
92-
};
111+
});

src/components/QueryEditor/MetricAggregationsEditor/state/reducer.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Action } from '@reduxjs/toolkit';
2-
import { defaultLogsAgg, defaultMetricAgg } from '@/queryDef';
2+
import { defaultMetricAgg } from '@/queryDef';
33
import { ElasticsearchQuery, MetricAggregation } from '@/types';
44
import { removeEmpty } from '@/utils';
55
import { initExploreQuery, initQuery } from '../../state';
@@ -17,7 +17,11 @@ import {
1717
toggleMetricVisibility,
1818
} from './actions';
1919

20+
import { store } from "@/store"
21+
2022
export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): ElasticsearchQuery['metrics'] => {
23+
const defaultsMetricAggregation = store.getState().defaults.metricAggregation;
24+
2125
if (addMetric.match(action)) {
2226
return [...state!, defaultMetricAgg(action.payload)];
2327
}
@@ -55,7 +59,7 @@ export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): E
5559
return {
5660
id: metric.id,
5761
type: action.payload.type,
58-
...metricAggregationConfig[action.payload.type].defaults,
62+
...defaultsMetricAggregation[action.payload.type as keyof typeof defaultsMetricAggregation],
5963
} as MetricAggregation;
6064
});
6165
}
@@ -164,7 +168,7 @@ export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): E
164168
if (state && state.length > 0) {
165169
return state;
166170
}
167-
return [defaultLogsAgg('3')];
171+
return [{ type: 'logs', id: '3', ...defaultsMetricAggregation.logs }];
168172
}
169173

170174
return state;

src/components/QueryEditor/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const ElasticSearchQueryField = ({ value, onChange, onSubmit }: ElasticSe
8484
};
8585

8686
const QueryEditorForm = ({ value, onRunQuery }: Props) => {
87+
8788
const dispatch = useDispatch();
8889
const nextId = useNextId();
8990
const styles = useStyles2(getStyles);

src/configuration/ConfigEditor.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { QuickwitOptions } from '../quickwit';
55
import { coerceOptions } from './utils';
66
import { Divider } from '../components/Divider';
77
import { DataLinks } from './DataLinks';
8+
import _ from 'lodash';
89

910
interface Props extends DataSourcePluginOptionsEditorProps<QuickwitOptions> {}
1011

@@ -92,6 +93,17 @@ export const QuickwitDetails = ({ value, onChange }: DetailsProps) => {
9293
/>
9394
</InlineField>
9495
</FieldSet>
96+
<FieldSet label="Editor settings">
97+
<InlineField label="Default logs limit" labelWidth={26} tooltip="The log level field must be a fast field">
98+
<Input
99+
id="quickwit_defaults_metricaggregation_logs_limit"
100+
value={value.jsonData.queryEditorConfig?.defaults?.['metricAggregation.logs.settings.limit']}
101+
onChange={(event) => onChange(_.merge(value, {jsonData:{queryEditorConfig:{defaults:{'metricAggregation.logs.settings.limit':event.currentTarget.value}}}}))}
102+
placeholder="100"
103+
width={40}
104+
/>
105+
</InlineField>
106+
</FieldSet>
95107
</div>
96108
</>
97109
);

src/datasource/base.ts

+5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { SECOND } from 'utils/time';
3737
import { GConstructor } from 'utils/mixins';
3838
import { LuceneQuery } from '@/utils/lucene';
3939
import { uidMaker } from "@/utils/uid"
40+
import { DefaultsConfigOverrides } from 'store/defaults/conf';
4041

4142
export type BaseQuickwitDataSourceConstructor = GConstructor<BaseQuickwitDataSource>
4243

@@ -59,6 +60,9 @@ export class BaseQuickwitDataSource
5960
logMessageField?: string;
6061
logLevelField?: string;
6162
dataLinks: DataLinkConfig[];
63+
queryEditorConfig?: {
64+
defaults?: DefaultsConfigOverrides
65+
};
6266
languageProvider: ElasticsearchLanguageProvider;
6367

6468

@@ -73,6 +77,7 @@ export class BaseQuickwitDataSource
7377
this.logMessageField = settingsData.logMessageField || '';
7478
this.logLevelField = settingsData.logLevelField || '';
7579
this.dataLinks = settingsData.dataLinks || [];
80+
this.queryEditorConfig = settingsData.queryEditorConfig || {};
7681
this.languageProvider = new ElasticsearchLanguageProvider(this);
7782
this.annotations = {};
7883
}

src/queryDef.ts

-4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ export function defaultMetricAgg(id = '1'): MetricAggregation {
3131
return { type: 'count', id };
3232
}
3333

34-
export function defaultLogsAgg(id = '1'): MetricAggregation {
35-
return { type: 'logs', id, ...metricAggregationConfig['logs'].defaults };
36-
}
37-
3834
export function defaultBucketAgg(id = '1'): DateHistogram {
3935
return { type: 'date_histogram', id, settings: { interval: 'auto' } };
4036
}

src/quickwit.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { DataSourceJsonData } from "@grafana/data";
22
import { DataLinkConfig } from "./types";
3+
import { DefaultsConfigOverrides } from "store/defaults/conf";
34

45
export interface QuickwitOptions extends DataSourceJsonData {
56
timeField: string;
@@ -8,4 +9,7 @@ export interface QuickwitOptions extends DataSourceJsonData {
89
logLevelField?: string;
910
dataLinks?: DataLinkConfig[];
1011
index: string;
12+
queryEditorConfig?: {
13+
defaults?: DefaultsConfigOverrides
14+
}
1115
}

src/store/defaults/conf.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
MetricAggregation,
3+
MetricAggregationType,
4+
Logs as SchemaLogs,
5+
} from '@/dataquery.gen';
6+
import { Logs, LogsSortDirection } from "@/types";
7+
8+
export type QuickwitMetricAggregationType = Extract<'count' | 'avg' | 'sum' | 'min' | 'max' | 'percentiles' | 'raw_data' | 'logs', MetricAggregationType >
9+
10+
export type MetricsDefaultSettings = Partial<{
11+
[T in QuickwitMetricAggregationType]: Omit<Extract<Exclude<MetricAggregation,SchemaLogs>|Logs, { type: T }>, 'id' | 'type'>;
12+
}>;
13+
14+
15+
export const defaultMetricAggregationConfig: MetricsDefaultSettings = {
16+
percentiles: {
17+
settings: {
18+
percents: ['25', '50', '75', '95', '99'],
19+
},
20+
},
21+
raw_data: {
22+
settings: {
23+
size: '100',
24+
},
25+
},
26+
logs: {
27+
settings: {
28+
limit: '100',
29+
sortDirection:'desc' as LogsSortDirection
30+
},
31+
},
32+
};
33+
34+
export const defaultConfig = {
35+
metricAggregation: defaultMetricAggregationConfig
36+
};
37+
38+
export type DefaultsConfig = typeof defaultConfig
39+
40+
export type DefaultsConfigOverrides = {[key: string]: any};

src/store/defaults/index.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
2+
import { defaultConfig , DefaultsConfigOverrides } from "./conf";
3+
import _ from "lodash";
4+
5+
export const initialState = defaultConfig
6+
7+
const defaultsSlice = createSlice({
8+
name: "defaults",
9+
initialState: defaultConfig,
10+
reducers: {
11+
initDefaults(_s, action: PayloadAction<DefaultsConfigOverrides | undefined>) {
12+
// Initialize from default state, dont keep the old one
13+
let newState = _.cloneDeep(defaultConfig);
14+
// override values with payload
15+
if (action.payload) {
16+
const overrides = action.payload;
17+
for (const key in overrides) {
18+
// XXX : this is very not type-safe. Can do better ?
19+
const value = overrides[key];
20+
newState = _.set(newState, key, value);
21+
}
22+
}
23+
return newState
24+
}
25+
}
26+
})
27+
28+
const {actions, reducer} = defaultsSlice
29+
export const {
30+
initDefaults,
31+
} = actions
32+
33+
export default reducer;

src/store/index.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { configureStore } from "@reduxjs/toolkit";
2+
import defaultsReducer from "./defaults"
3+
4+
export const store = configureStore({
5+
reducer: {
6+
defaults: defaultsReducer,
7+
}
8+
})
9+
10+
// Infer the `RootState` and `AppDispatch` types from the store itself
11+
export type RootState = ReturnType<typeof store.getState>
12+
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
13+
export type AppDispatch = typeof store.dispatch

0 commit comments

Comments
 (0)