ydb-embedded-ui 3.5.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
  3. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
  4. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
  5. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
  6. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
  7. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
  8. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
  9. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
  10. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
  11. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
  12. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
  13. package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
  14. package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
  15. package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
  16. package/dist/containers/UserSettings/UserSettings.tsx +30 -1
  17. package/dist/services/api.d.ts +4 -3
  18. package/dist/services/api.js +2 -2
  19. package/dist/store/reducers/executeQuery.ts +12 -37
  20. package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
  21. package/dist/store/reducers/settings.js +18 -3
  22. package/dist/types/api/error.ts +14 -0
  23. package/dist/types/api/query.ts +226 -117
  24. package/dist/types/store/executeQuery.ts +4 -8
  25. package/dist/types/store/explainQuery.ts +38 -0
  26. package/dist/types/store/query.ts +23 -3
  27. package/dist/utils/constants.ts +2 -1
  28. package/dist/utils/error.ts +25 -0
  29. package/dist/utils/index.js +0 -49
  30. package/dist/utils/prepareQueryExplain.ts +7 -24
  31. package/dist/utils/query.test.ts +153 -231
  32. package/dist/utils/query.ts +44 -78
  33. package/dist/utils/timeParsers/i18n/en.json +9 -9
  34. package/dist/utils/timeParsers/i18n/ru.json +9 -9
  35. package/dist/utils/timeParsers/parsers.ts +9 -0
  36. package/dist/utils/utils.js +1 -2
  37. package/package.json +1 -1
@@ -5,12 +5,7 @@ import MonacoEditor from 'react-monaco-editor';
5
5
  import JSONTree from 'react-json-inspector';
6
6
  import 'react-json-inspector/json-inspector.css';
7
7
 
8
- import {
9
- TextOverflow,
10
- getYdbPlanNodeShape,
11
- getCompactTopology,
12
- getTopology,
13
- } from '@gravity-ui/paranoid';
8
+ import {TextOverflow, getYdbPlanNodeShape, getTopology} from '@gravity-ui/paranoid';
14
9
  import {Loader, RadioButton} from '@gravity-ui/uikit';
15
10
 
16
11
  import Divider from '../../../../components/Divider/Divider';
@@ -55,7 +50,7 @@ const explainOptions = [
55
50
  function GraphRoot(props) {
56
51
  const paranoid = useRef();
57
52
 
58
- const {data, opts, shapes, version, theme} = props;
53
+ const {data, opts, shapes, theme} = props;
59
54
 
60
55
  const [componentTheme, updateComponentTheme] = useState(theme);
61
56
 
@@ -64,13 +59,8 @@ function GraphRoot(props) {
64
59
  }, [theme]);
65
60
 
66
61
  const render = () => {
67
- if (version === explainVersions.v2) {
68
- paranoid.current = getTopology('graphRoot', data, opts, shapes);
69
- paranoid.current.render();
70
- } else if (version === explainVersions.v1) {
71
- paranoid.current = getCompactTopology('graphRoot', data, opts);
72
- paranoid.current.renderCompactTopology();
73
- }
62
+ paranoid.current = getTopology('graphRoot', data, opts, shapes);
63
+ paranoid.current.render();
74
64
  };
75
65
 
76
66
  useEffect(() => {
@@ -112,12 +102,6 @@ function QueryExplain(props) {
112
102
  };
113
103
  }, []);
114
104
 
115
- useEffect(() => {
116
- if (!props.ast && activeOption === ExplainOptionIds.ast) {
117
- props.astQuery();
118
- }
119
- }, [activeOption]);
120
-
121
105
  const onSelectOption = (tabId) => {
122
106
  setActiveOption(tabId);
123
107
  };
@@ -131,7 +115,9 @@ function QueryExplain(props) {
131
115
  };
132
116
 
133
117
  const renderStub = () => {
134
- return <div className={b('text-message')}>There is no explanation for the request</div>;
118
+ return (
119
+ <div className={b('text-message')}>{`There is no ${activeOption} for the request`}</div>
120
+ );
135
121
  };
136
122
 
137
123
  const hasContent = () => {
@@ -189,8 +175,12 @@ function QueryExplain(props) {
189
175
  const renderGraph = () => {
190
176
  const {explain = {}, theme} = props;
191
177
  const {links, nodes, version} = explain;
178
+
179
+ const isSupportedVersion = version === explainVersions.v2;
180
+ const isEnoughDataForGraph = links && nodes && nodes.length;
181
+
192
182
  const content =
193
- links && nodes && nodes.length ? (
183
+ isSupportedVersion && isEnoughDataForGraph ? (
194
184
  <div
195
185
  className={b('explain-canvas-container', {
196
186
  hidden: activeOption !== ExplainOptionIds.schema,
@@ -198,7 +188,6 @@ function QueryExplain(props) {
198
188
  >
199
189
  <GraphRoot
200
190
  theme={theme}
201
- version={version}
202
191
  data={{links, nodes}}
203
192
  opts={{
204
193
  renderNodeTitle: renderExplainNode,
@@ -18,6 +18,7 @@ import {prepareQueryError} from '../../../../utils/query';
18
18
  import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
19
19
 
20
20
  import ResultIssues from '../Issues/Issues';
21
+ import {QueryDuration} from '../QueryDuration/QueryDuration';
21
22
 
22
23
  import './QueryResult.scss';
23
24
 
@@ -116,15 +117,11 @@ function QueryResult(props) {
116
117
  </Fullscreen>
117
118
  )}
118
119
  </React.Fragment>
119
- )
120
+ );
120
121
  }
121
122
 
122
123
  if (error) {
123
- return (
124
- <div className={b('error')}>
125
- {prepareQueryError(error)}
126
- </div>
127
- );
124
+ return <div className={b('error')}>{prepareQueryError(error)}</div>;
128
125
  }
129
126
  };
130
127
 
@@ -136,6 +133,7 @@ function QueryResult(props) {
136
133
 
137
134
  {props.stats && !props.error && (
138
135
  <React.Fragment>
136
+ <QueryDuration duration={props.stats?.DurationUs} />
139
137
  <Divider />
140
138
  <RadioButton
141
139
  options={resultOptions}
@@ -0,0 +1,3 @@
1
+ {
2
+ "controls.query-mode-selector_type": "Type:"
3
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-query-editor';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,3 @@
1
+ {
2
+ "controls.query-mode-selector_type": "Тип:"
3
+ }
@@ -9,6 +9,7 @@ import favoriteFilledIcon from '../../assets/icons/star.svg';
9
9
  import flaskIcon from '../../assets/icons/flask.svg';
10
10
 
11
11
  import {
12
+ ENABLE_QUERY_MODES_FOR_EXPLAIN,
12
13
  INVERTED_DISKS_KEY,
13
14
  THEME_KEY,
14
15
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
@@ -39,6 +40,10 @@ function UserSettings(props: any) {
39
40
  props.setSettingValue(USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, String(value));
40
41
  };
41
42
 
43
+ const _onExplainQueryModesChangeHandler = (value: boolean) => {
44
+ props.setSettingValue(ENABLE_QUERY_MODES_FOR_EXPLAIN, String(value));
45
+ };
46
+
42
47
  const renderBreakNodesSettingsItem = (title: ReactNode) => {
43
48
  return (
44
49
  <div className={b('item-with-popup')}>
@@ -52,6 +57,19 @@ function UserSettings(props: any) {
52
57
  );
53
58
  };
54
59
 
60
+ const renderEnableExplainQueryModesItem = (title: ReactNode) => {
61
+ return (
62
+ <div className={b('item-with-popup')}>
63
+ {title}
64
+ <HelpPopover
65
+ content="Enable script | scan query mode selector for both run and explain. May not work on some versions"
66
+ contentClassName={b('popup')}
67
+ hasArrow={true}
68
+ />
69
+ </div>
70
+ );
71
+ };
72
+
55
73
  return (
56
74
  <Settings>
57
75
  <Settings.Page
@@ -86,6 +104,15 @@ function UserSettings(props: any) {
86
104
  onUpdate={_onNodesEndpointChangeHandler}
87
105
  />
88
106
  </Settings.Item>
107
+ <Settings.Item
108
+ title="Enable query modes for explain"
109
+ renderTitleComponent={renderEnableExplainQueryModesItem}
110
+ >
111
+ <Switch
112
+ checked={props.enableQueryModesForExplain}
113
+ onUpdate={_onExplainQueryModesChangeHandler}
114
+ />
115
+ </Settings.Item>
89
116
  </Settings.Section>
90
117
  </Settings.Page>
91
118
  </Settings>
@@ -93,12 +120,14 @@ function UserSettings(props: any) {
93
120
  }
94
121
 
95
122
  const mapStateToProps = (state: any) => {
96
- const {theme, invertedDisks, useNodesEndpointInDiagnostics} = state.settings.userSettings;
123
+ const {theme, invertedDisks, useNodesEndpointInDiagnostics, enableQueryModesForExplain} =
124
+ state.settings.userSettings;
97
125
 
98
126
  return {
99
127
  theme,
100
128
  invertedDisks: JSON.parse(invertedDisks),
101
129
  useNodesEndpointInDiagnostics: JSON.parse(useNodesEndpointInDiagnostics),
130
+ enableQueryModesForExplain: JSON.parse(enableQueryModesForExplain),
102
131
  };
103
132
  };
104
133
 
@@ -38,14 +38,15 @@ interface Window {
38
38
  },
39
39
  axiosOptions?: AxiosOptions,
40
40
  ) => Promise<import('../types/api/query').QueryAPIResponse<Action, Schema>>;
41
- getExplainQuery: (
41
+ getExplainQuery: <Action extends import('../types/api/query').ExplainActions = 'explain'>(
42
42
  query: string,
43
43
  database: string,
44
- ) => Promise<import('../types/api/query').QueryAPIExplainResponse<'explain'>>;
44
+ action?: Action,
45
+ ) => Promise<import('../types/api/query').ExplainResponse<Action>>;
45
46
  getExplainQueryAst: (
46
47
  query: string,
47
48
  database: string,
48
- ) => Promise<import('../types/api/query').QueryAPIExplainResponse<'explain-ast'>>;
49
+ ) => Promise<import('../types/api/query').ExplainResponse<'explain-ast'>>;
49
50
  getHealthcheckInfo: (
50
51
  database: string,
51
52
  ) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>;
@@ -207,11 +207,11 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
207
207
  },
208
208
  );
209
209
  }
210
- getExplainQuery(query, database) {
210
+ getExplainQuery(query, database, action = 'explain') {
211
211
  return this.post(this.getPath('/viewer/json/query'), {
212
212
  query,
213
213
  database,
214
- action: 'explain',
214
+ action,
215
215
  timeout: 600000,
216
216
  });
217
217
  }
@@ -1,21 +1,20 @@
1
1
  import type {Reducer} from 'redux';
2
2
 
3
- import type {Actions} from '../../types/api/query';
3
+ import type {ExecuteActions} from '../../types/api/query';
4
4
  import type {
5
5
  ExecuteQueryAction,
6
6
  ExecuteQueryState,
7
7
  MonacoHotKeyAction,
8
- RunAction,
9
8
  } from '../../types/store/executeQuery';
9
+ import type {QueryRequestParams, QueryModes} from '../../types/store/query';
10
10
  import {getValueFromLS, parseJson} from '../../utils/utils';
11
- import {QUERIES_HISTORY_KEY, QUERY_INITIAL_RUN_ACTION_KEY} from '../../utils/constants';
11
+ import {QUERIES_HISTORY_KEY} from '../../utils/constants';
12
12
  import {parseQueryAPIExecuteResponse} from '../../utils/query';
13
+ import {parseQueryError} from '../../utils/error';
13
14
  import '../../services/api';
14
15
 
15
16
  import {createRequestActionTypes, createApiRequest} from '../utils';
16
17
 
17
- import {readSavedSettingsValue} from './settings';
18
-
19
18
  const MAXIMUM_QUERIES_IN_HISTORY = 20;
20
19
 
21
20
  export const SEND_QUERY = createRequestActionTypes('query', 'SEND_QUERY');
@@ -24,18 +23,12 @@ const CHANGE_USER_INPUT = 'query/CHANGE_USER_INPUT';
24
23
  const SAVE_QUERY_TO_HISTORY = 'query/SAVE_QUERY_TO_HISTORY';
25
24
  const GO_TO_PREVIOUS_QUERY = 'query/GO_TO_PREVIOUS_QUERY';
26
25
  const GO_TO_NEXT_QUERY = 'query/GO_TO_NEXT_QUERY';
27
- const SELECT_RUN_ACTION = 'query/SELECT_RUN_ACTION';
28
26
  const MONACO_HOT_KEY = 'query/MONACO_HOT_KEY';
29
27
 
30
28
  const queriesHistoryInitial: string[] = parseJson(getValueFromLS(QUERIES_HISTORY_KEY, '[]'));
31
29
 
32
30
  const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY;
33
31
 
34
- export const RUN_ACTIONS_VALUES = {
35
- script: 'execute-script',
36
- scan: 'execute-scan',
37
- } as const;
38
-
39
32
  export const MONACO_HOT_KEY_ACTIONS = {
40
33
  sendQuery: 'sendQuery',
41
34
  goPrev: 'goPrev',
@@ -53,7 +46,6 @@ const initialState = {
53
46
  ? MAXIMUM_QUERIES_IN_HISTORY - 1
54
47
  : queriesHistoryInitial.length - 1,
55
48
  },
56
- runAction: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY, RUN_ACTIONS_VALUES.script),
57
49
  monacoHotKey: null,
58
50
  };
59
51
 
@@ -79,22 +71,14 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
79
71
  error: undefined,
80
72
  };
81
73
  }
82
- // 401 Unauthorized error is handled by GenericAPI
83
74
  case SEND_QUERY.FAILURE: {
84
75
  return {
85
76
  ...state,
86
- error: action.error || 'Unauthorized',
77
+ error: parseQueryError(action.error),
87
78
  loading: false,
88
79
  };
89
80
  }
90
81
 
91
- case SELECT_RUN_ACTION: {
92
- return {
93
- ...state,
94
- runAction: action.data,
95
- };
96
- }
97
-
98
82
  case CHANGE_USER_INPUT: {
99
83
  return {
100
84
  ...state,
@@ -156,15 +140,13 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
156
140
  }
157
141
  };
158
142
 
159
- export const sendQuery = ({
160
- query,
161
- database,
162
- action,
163
- }: {
164
- query: string;
165
- database: string;
166
- action: Actions;
167
- }) => {
143
+ interface SendQueryParams extends QueryRequestParams {
144
+ mode?: QueryModes;
145
+ }
146
+
147
+ export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
148
+ const action: ExecuteActions = mode ? `execute-${mode}` : 'execute';
149
+
168
150
  return createApiRequest({
169
151
  request: window.api.sendQuery({
170
152
  schema: 'modern',
@@ -185,13 +167,6 @@ export const saveQueryToHistory = (query: string) => {
185
167
  } as const;
186
168
  };
187
169
 
188
- export const selectRunAction = (value: RunAction) => {
189
- return {
190
- type: SELECT_RUN_ACTION,
191
- data: value,
192
- } as const;
193
- };
194
-
195
170
  export const goToPreviousQuery = () => {
196
171
  return {
197
172
  type: GO_TO_PREVIOUS_QUERY,
@@ -1,21 +1,33 @@
1
+ import type {Reducer} from 'redux';
2
+ import type {ExplainPlanNodeData, GraphNode, Link} from '@gravity-ui/paranoid';
1
3
  import _ from 'lodash';
2
4
 
3
5
  import '../../services/api';
6
+ import type {ExplainActions} from '../../types/api/query';
7
+ import type {
8
+ ExplainQueryAction,
9
+ ExplainQueryState,
10
+ PreparedExplainResponse,
11
+ } from '../../types/store/explainQuery';
12
+ import type {QueryRequestParams, QueryModes} from '../../types/store/query';
4
13
 
5
- import {getExplainNodeId, getMetaForExplainNode} from '../../utils';
6
14
  import {preparePlan} from '../../utils/prepareQueryExplain';
7
- import {parseQueryAPIExplainResponse} from '../../utils/query';
15
+ import {parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../utils/query';
16
+ import {parseQueryError} from '../../utils/error';
8
17
 
9
18
  import {createRequestActionTypes, createApiRequest} from '../utils';
10
19
 
11
- const GET_EXPLAIN_QUERY = createRequestActionTypes('query', 'GET_EXPLAIN_QUERY');
12
- const GET_EXPLAIN_QUERY_AST = createRequestActionTypes('query', 'GET_EXPLAIN_QUERY_AST');
20
+ export const GET_EXPLAIN_QUERY = createRequestActionTypes('query', 'GET_EXPLAIN_QUERY');
21
+ export const GET_EXPLAIN_QUERY_AST = createRequestActionTypes('query', 'GET_EXPLAIN_QUERY_AST');
13
22
 
14
23
  const initialState = {
15
24
  loading: false,
16
25
  };
17
26
 
18
- const explainQuery = (state = initialState, action) => {
27
+ const explainQuery: Reducer<ExplainQueryState, ExplainQueryAction> = (
28
+ state = initialState,
29
+ action,
30
+ ) => {
19
31
  switch (action.type) {
20
32
  case GET_EXPLAIN_QUERY.REQUEST: {
21
33
  return {
@@ -36,11 +48,10 @@ const explainQuery = (state = initialState, action) => {
36
48
  error: undefined,
37
49
  };
38
50
  }
39
- // 401 Unauthorized error is handled by GenericAPI
40
51
  case GET_EXPLAIN_QUERY.FAILURE: {
41
52
  return {
42
53
  ...state,
43
- error: action.error || 'Unauthorized',
54
+ error: parseQueryError(action.error),
44
55
  loading: false,
45
56
  };
46
57
  }
@@ -63,7 +74,7 @@ const explainQuery = (state = initialState, action) => {
63
74
  case GET_EXPLAIN_QUERY_AST.FAILURE: {
64
75
  return {
65
76
  ...state,
66
- errorAst: action.error || 'Unauthorized',
77
+ errorAst: parseQueryError(action.error),
67
78
  loadingAst: false,
68
79
  };
69
80
  }
@@ -73,7 +84,7 @@ const explainQuery = (state = initialState, action) => {
73
84
  }
74
85
  };
75
86
 
76
- export const getExplainQueryAst = ({query, database}) => {
87
+ export const getExplainQueryAst = ({query, database}: QueryRequestParams) => {
77
88
  return createApiRequest({
78
89
  request: window.api.getExplainQueryAst(query, database),
79
90
  actions: GET_EXPLAIN_QUERY_AST,
@@ -82,74 +93,48 @@ export const getExplainQueryAst = ({query, database}) => {
82
93
  };
83
94
 
84
95
  export const explainVersions = {
85
- v1: '0.1',
86
96
  v2: '0.2',
87
97
  };
88
98
 
89
99
  const supportedExplainQueryVersions = Object.values(explainVersions);
90
100
 
91
- export const getExplainQuery = ({query, database}) => {
101
+ interface ExplainQueryParams extends QueryRequestParams {
102
+ mode?: QueryModes;
103
+ }
104
+
105
+ export const getExplainQuery = ({query, database, mode}: ExplainQueryParams) => {
106
+ const action: ExplainActions = mode ? `explain-${mode}` : 'explain';
107
+
92
108
  return createApiRequest({
93
- request: window.api.getExplainQuery(query, database),
109
+ request: window.api.getExplainQuery(query, database, action),
94
110
  actions: GET_EXPLAIN_QUERY,
95
- dataHandler: (response) => {
96
- const {plan: result, ast} = parseQueryAPIExplainResponse(response);
111
+ dataHandler: (response): PreparedExplainResponse => {
112
+ const {plan: rawPlan, ast} = parseQueryAPIExplainResponse(response);
97
113
 
98
- if (!result) {
114
+ if (!rawPlan) {
99
115
  return {ast};
100
116
  }
101
117
 
102
- let links = [];
103
- let nodes = [];
104
- const {tables, meta, Plan} = result;
118
+ const {tables, meta, Plan} = parseQueryExplainPlan(rawPlan);
105
119
 
106
120
  if (supportedExplainQueryVersions.indexOf(meta.version) === -1) {
121
+ // Do not prepare plan for not supported versions
107
122
  return {
108
- pristine: result,
109
- version: meta.version,
123
+ plan: {
124
+ pristine: rawPlan,
125
+ version: meta.version,
126
+ },
127
+ ast,
110
128
  };
111
129
  }
112
- if (meta.version === explainVersions.v2) {
130
+
131
+ let links: Link[] = [];
132
+ let nodes: GraphNode<ExplainPlanNodeData>[] = [];
133
+
134
+ if (Plan) {
113
135
  const preparedPlan = preparePlan(Plan);
114
136
  links = preparedPlan.links;
115
137
  nodes = preparedPlan.nodes;
116
- } else {
117
- _.forEach(tables, (table) => {
118
- nodes.push({
119
- name: table.name,
120
- });
121
-
122
- const tableTypes = {};
123
-
124
- const {reads = [], writes = []} = table;
125
- let prevNodeId = table.name;
126
-
127
- _.forEach([...reads, ...writes], (node) => {
128
- if (tableTypes[node.type]) {
129
- tableTypes[node.type] = tableTypes[node.type] + 1;
130
- } else {
131
- tableTypes[node.type] = 1;
132
- }
133
-
134
- const nodeId = getExplainNodeId(
135
- table.name,
136
- node.type,
137
- tableTypes[node.type],
138
- );
139
-
140
- links.push({
141
- from: prevNodeId,
142
- to: nodeId,
143
- });
144
- nodes.push({
145
- name: nodeId,
146
- meta: getMetaForExplainNode(node),
147
- id: nodeId,
148
- });
149
-
150
- prevNodeId = nodeId;
151
- });
152
- });
153
138
  }
154
139
 
155
140
  return {
@@ -158,7 +143,7 @@ export const getExplainQuery = ({query, database}) => {
158
143
  nodes,
159
144
  tables,
160
145
  version: meta.version,
161
- pristine: result,
146
+ pristine: rawPlan,
162
147
  },
163
148
  ast,
164
149
  };
@@ -3,14 +3,16 @@ import {
3
3
  SAVED_QUERIES_KEY,
4
4
  THEME_KEY,
5
5
  TENANT_INITIAL_TAB_KEY,
6
- QUERY_INITIAL_RUN_ACTION_KEY,
7
6
  INVERTED_DISKS_KEY,
8
7
  ASIDE_HEADER_COMPACT_KEY,
9
8
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
10
9
  PARTITIONS_SELECTED_COLUMNS_KEY,
10
+ QUERY_INITIAL_MODE_KEY,
11
+ ENABLE_QUERY_MODES_FOR_EXPLAIN,
11
12
  } from '../../utils/constants';
12
13
  import '../../services/api';
13
- import {getValueFromLS} from '../../utils/utils';
14
+ import {getValueFromLS, parseJson} from '../../utils/utils';
15
+ import {QueryModes} from '../../types/store/query';
14
16
 
15
17
  const CHANGE_PROBLEM_FILTER = 'settings/CHANGE_PROBLEM_FILTER';
16
18
  const SET_SETTING_VALUE = 'settings/SET_VALUE';
@@ -44,9 +46,13 @@ export const initialState = {
44
46
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
45
47
  'false',
46
48
  ),
49
+ [ENABLE_QUERY_MODES_FOR_EXPLAIN]: readSavedSettingsValue(
50
+ ENABLE_QUERY_MODES_FOR_EXPLAIN,
51
+ 'false',
52
+ ),
47
53
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
48
54
  [TENANT_INITIAL_TAB_KEY]: readSavedSettingsValue(TENANT_INITIAL_TAB_KEY),
49
- [QUERY_INITIAL_RUN_ACTION_KEY]: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY),
55
+ [QUERY_INITIAL_MODE_KEY]: readSavedSettingsValue(QUERY_INITIAL_MODE_KEY, QueryModes.script),
50
56
  [ASIDE_HEADER_COMPACT_KEY]: readSavedSettingsValue(
51
57
  ASIDE_HEADER_COMPACT_KEY,
52
58
  legacyAsideNavCompactState || 'true',
@@ -97,6 +103,15 @@ export const getSettingValue = (state, name) => {
97
103
  return state.settings.userSettings[name];
98
104
  };
99
105
 
106
+ /**
107
+ * Returns parsed settings value.
108
+ * If value cannot be parsed, returns initially stored string
109
+ */
110
+ export const getParsedSettingValue = (state, name) => {
111
+ const value = state.settings.userSettings[name];
112
+ return parseJson(value);
113
+ };
114
+
100
115
  export const changeFilter = (filter) => {
101
116
  return {
102
117
  type: CHANGE_PROBLEM_FILTER,
@@ -4,3 +4,17 @@ export interface IResponseError {
4
4
  statusText?: string;
5
5
  isCancelled?: boolean;
6
6
  }
7
+
8
+ // Error on offline backend or requests blocked by CORS
9
+ export interface NetworkError {
10
+ code?: unknown;
11
+ columnNumber?: unknown;
12
+ config?: Record<string, unknown>;
13
+ description?: unknown;
14
+ fileName?: unknown;
15
+ lineNumber?: unknown;
16
+ message?: 'Network Error';
17
+ name?: string;
18
+ number?: unknown;
19
+ stack?: string;
20
+ }