ydb-embedded-ui 3.5.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }