datajunction-ui 0.0.1-rc.11 → 0.0.1-rc.13

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.
package/.env CHANGED
@@ -1 +1,2 @@
1
1
  REACT_APP_DJ_URL=http://localhost:8000
2
+ REACT_USE_SSE=true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datajunction-ui",
3
- "version": "0.0.1-rc.11",
3
+ "version": "0.0.1-rc.13",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -35,9 +35,11 @@ exports[`<App /> should render and match the snapshot 1`] = `
35
35
  "namespaces": [Function],
36
36
  "node": [Function],
37
37
  "node_dag": [Function],
38
+ "nodesWithDimension": [Function],
38
39
  "revisions": [Function],
39
40
  "sql": [Function],
40
41
  "sqls": [Function],
42
+ "stream": [Function],
41
43
  "upstreams": [Function],
42
44
  },
43
45
  }
@@ -10,38 +10,6 @@ export default function QueryInfo({
10
10
  started,
11
11
  numRows,
12
12
  }) {
13
- const stateIcon =
14
- state === 'FINISHED' ? (
15
- <span className="status__valid status" style={{ alignContent: 'center' }}>
16
- <svg
17
- xmlns="http://www.w3.org/2000/svg"
18
- width="25"
19
- height="25"
20
- fill="currentColor"
21
- className="bi bi-check-circle-fill"
22
- viewBox="0 0 16 16"
23
- >
24
- <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
25
- </svg>
26
- </span>
27
- ) : (
28
- <span
29
- className="status__invalid status"
30
- style={{ alignContent: 'center' }}
31
- >
32
- <svg
33
- xmlns="http://www.w3.org/2000/svg"
34
- width="16"
35
- height="16"
36
- fill="currentColor"
37
- className="bi bi-x-circle-fill"
38
- viewBox="0 0 16 16"
39
- >
40
- <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z" />
41
- </svg>
42
- </span>
43
- );
44
-
45
13
  return (
46
14
  <div className="table-responsive">
47
15
  <table className="card-inner-table table">
@@ -70,11 +38,11 @@ export default function QueryInfo({
70
38
  {engine_version}
71
39
  </span>
72
40
  </td>
73
- <td>{stateIcon}</td>
41
+ <td>{state}</td>
74
42
  <td>{scheduled}</td>
75
43
  <td>{started}</td>
76
44
  <td>
77
- {errors.length ? (
45
+ {errors?.length ? (
78
46
  errors.map(e => (
79
47
  <p>
80
48
  <span className="rounded-pill badge bg-secondary-error">
@@ -20,7 +20,7 @@ export function NamespacePage() {
20
20
  const hierarchy = [];
21
21
 
22
22
  for (const item of namespaceList) {
23
- const namespaces = item.namespace.split('.');
23
+ const namespaces = item.split('.');
24
24
  let currentLevel = hierarchy;
25
25
 
26
26
  let path = '';
@@ -13,6 +13,107 @@ export default function NodeHistory({ node, djClient }) {
13
13
  };
14
14
  fetchData().catch(console.error);
15
15
  }, [djClient, node]);
16
+
17
+ const eventData = event => {
18
+ console.log('event', event);
19
+ if (
20
+ event.activity_type === 'set_attribute' &&
21
+ event.entity_type === 'column_attribute'
22
+ ) {
23
+ return event.details.attributes
24
+ .map(attr => (
25
+ <div>
26
+ Set{' '}
27
+ <span className={`badge partition_value`}>{attr.column_name}</span>{' '}
28
+ as{' '}
29
+ <span className={`badge partition_value_highlight`}>
30
+ {attr.attribute_type_name}
31
+ </span>
32
+ </div>
33
+ ))
34
+ .reduce((prev, curr) => [prev, <br />, curr]);
35
+ }
36
+ if (event.activity_type === 'create' && event.entity_type === 'link') {
37
+ return (
38
+ <div>
39
+ Linked{' '}
40
+ <span className={`badge partition_value`}>
41
+ {event.details.column}
42
+ </span>{' '}
43
+ to
44
+ <span className={`badge partition_value_highlight`}>
45
+ {event.details.dimension}
46
+ </span>{' '}
47
+ via
48
+ <span className={`badge partition_value`}>
49
+ {event.details.dimension_column}
50
+ </span>
51
+ </div>
52
+ );
53
+ }
54
+ if (
55
+ event.activity_type === 'create' &&
56
+ event.entity_type === 'materialization'
57
+ ) {
58
+ return (
59
+ <div>
60
+ Initialized materialization{' '}
61
+ <span className={`badge partition_value`}>
62
+ {event.details.materialization}
63
+ </span>
64
+ </div>
65
+ );
66
+ }
67
+ if (
68
+ event.activity_type === 'create' &&
69
+ event.entity_type === 'availability'
70
+ ) {
71
+ return (
72
+ <div>
73
+ Materialized at{' '}
74
+ <span className={`badge partition_value_highlight`}>
75
+ {event.post.catalog}.{event.post.schema_}.{event.post.table}
76
+ </span>
77
+ from{' '}
78
+ <span className={`badge partition_value`}>
79
+ {event.post.min_temporal_partition}
80
+ </span>{' '}
81
+ to
82
+ <span className={`badge partition_value`}>
83
+ {event.post.max_temporal_partition}
84
+ </span>
85
+ </div>
86
+ );
87
+ }
88
+ if (
89
+ event.activity_type === 'status_change' &&
90
+ event.entity_type === 'node'
91
+ ) {
92
+ const expr = (
93
+ <div>
94
+ Caused by a change in upstream{' '}
95
+ <a href={`/nodes/${event.details['upstream_node']}`}>
96
+ {event.details['upstream_node']}
97
+ </a>
98
+ </div>
99
+ );
100
+ return (
101
+ <div>
102
+ Status changed from{' '}
103
+ <span className={`status__${event.pre['status']}`}>
104
+ {event.pre['status']}
105
+ </span>{' '}
106
+ to{' '}
107
+ <span className={`status__${event.post['status']}`}>
108
+ {event.post['status']}
109
+ </span>{' '}
110
+ {event.details['upstream_node'] !== undefined ? expr : ''}
111
+ </div>
112
+ );
113
+ }
114
+ return '';
115
+ };
116
+
16
117
  const tableData = history => {
17
118
  return history.map(event => (
18
119
  <tr>
@@ -24,9 +125,9 @@ export default function NodeHistory({ node, djClient }) {
24
125
  </span>
25
126
  </td>
26
127
  <td>{event.entity_type}</td>
27
- <td>{event.entity_name}</td>
28
128
  <td>{event.user ? event.user : 'unknown'}</td>
29
129
  <td>{event.created_at}</td>
130
+ <td>{eventData(event)}</td>
30
131
  </tr>
31
132
  ));
32
133
  };
@@ -45,7 +146,7 @@ export default function NodeHistory({ node, djClient }) {
45
146
  ));
46
147
  };
47
148
  return (
48
- <div className="table-responsive">
149
+ <div className="table-vertical">
49
150
  <table className="card-inner-table table">
50
151
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
51
152
  <th className="text-start">Version</th>
@@ -60,9 +161,9 @@ export default function NodeHistory({ node, djClient }) {
60
161
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
61
162
  <th className="text-start">Activity</th>
62
163
  <th>Type</th>
63
- <th>Name</th>
64
164
  <th>User</th>
65
165
  <th>Timestamp</th>
166
+ <th>Details</th>
66
167
  </thead>
67
168
  {tableData(history)}
68
169
  </table>
@@ -2,7 +2,6 @@ import { useState, useContext, useEffect } from 'react';
2
2
  import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
3
3
  import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
4
4
  import sql from 'react-syntax-highlighter/dist/esm/languages/hljs/sql';
5
- import { format } from 'sql-formatter';
6
5
  import NodeStatus from './NodeStatus';
7
6
  import ListGroupItem from '../../components/ListGroupItem';
8
7
  import ToggleSwitch from '../../components/ToggleSwitch';
@@ -50,23 +49,7 @@ export default function NodeInfoTab({ node }) {
50
49
  <></>
51
50
  )}
52
51
  <SyntaxHighlighter language="sql" style={foundation}>
53
- {checked
54
- ? format(compiledSQL, {
55
- language: 'spark',
56
- tabWidth: 2,
57
- keywordCase: 'upper',
58
- denseOperators: true,
59
- logicalOperatorNewline: 'before',
60
- expressionWidth: 10,
61
- })
62
- : format(node?.query, {
63
- language: 'spark',
64
- tabWidth: 2,
65
- keywordCase: 'upper',
66
- denseOperators: true,
67
- logicalOperatorNewline: 'before',
68
- expressionWidth: 10,
69
- })}
52
+ {checked ? compiledSQL : node?.query}
70
53
  </SyntaxHighlighter>
71
54
  </div>
72
55
  </div>
@@ -82,14 +82,7 @@ const NodeSQLTab = djNode => {
82
82
  >
83
83
  <h6 className="mb-0 w-100">Query</h6>
84
84
  <SyntaxHighlighter language="sql" style={foundation}>
85
- {format(query, {
86
- language: 'spark',
87
- tabWidth: 2,
88
- keywordCase: 'upper',
89
- denseOperators: true,
90
- logicalOperatorNewline: 'before',
91
- expressionWidth: 10,
92
- })}
85
+ {query}
93
86
  </SyntaxHighlighter>
94
87
  </div>
95
88
  </div>
@@ -0,0 +1,40 @@
1
+ import { useEffect, useState } from 'react';
2
+ import * as React from 'react';
3
+
4
+ export default function NodesWithDimension({ node, djClient }) {
5
+ const [availableNodes, setAvailableNodes] = useState([]);
6
+
7
+ useEffect(() => {
8
+ const fetchData = async () => {
9
+ const data = await djClient.nodesWithDimension(node.name);
10
+ setAvailableNodes(data);
11
+ };
12
+ fetchData().catch(console.error);
13
+ }, [djClient, node]);
14
+ return (
15
+ <div className="table-responsive">
16
+ <table className="card-inner-table table">
17
+ <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
18
+ <th className="text-start">Name</th>
19
+ <th>Type</th>
20
+ </thead>
21
+ <tbody>
22
+ {availableNodes.map(node => (
23
+ <tr>
24
+ <td>
25
+ <a href={`/nodes/${node.name}`}>{node.display_name}</a>
26
+ </td>
27
+ <td>
28
+ <span
29
+ className={'node_type__' + node.type + ' badge node_type'}
30
+ >
31
+ {node.type}
32
+ </span>
33
+ </td>
34
+ </tr>
35
+ ))}
36
+ </tbody>
37
+ </table>
38
+ </div>
39
+ );
40
+ }
@@ -11,6 +11,7 @@ import DJClientContext from '../../providers/djclient';
11
11
  import NodeSQLTab from './NodeSQLTab';
12
12
  import NodeMaterializationTab from './NodeMaterializationTab';
13
13
  import ClientCodePopover from './ClientCodePopover';
14
+ import NodesWithDimension from './NodesWithDimension';
14
15
 
15
16
  export function NodePage() {
16
17
  const djClient = useContext(DJClientContext).DataJunctionAPI;
@@ -25,7 +26,7 @@ export function NodePage() {
25
26
  };
26
27
 
27
28
  const buildTabs = tab => {
28
- return (
29
+ return tab.display ? (
29
30
  <Tab
30
31
  key={tab.id}
31
32
  id={tab.id}
@@ -33,7 +34,7 @@ export function NodePage() {
33
34
  onClick={onClickTab(tab.id)}
34
35
  selectedTab={state.selectedTab}
35
36
  />
36
- );
37
+ ) : null;
37
38
  };
38
39
 
39
40
  const { name } = useParams();
@@ -57,32 +58,46 @@ export function NodePage() {
57
58
  fetchData().catch(console.error);
58
59
  }, [djClient, name]);
59
60
 
60
- const TabsJson = [
61
- {
62
- id: 0,
63
- name: 'Info',
64
- },
65
- {
66
- id: 1,
67
- name: 'Columns',
68
- },
69
- {
70
- id: 2,
71
- name: 'Graph',
72
- },
73
- {
74
- id: 3,
75
- name: 'History',
76
- },
77
- {
78
- id: 4,
79
- name: 'SQL',
80
- },
81
- {
82
- id: 5,
83
- name: 'Materializations',
84
- },
85
- ];
61
+ const tabsList = node => {
62
+ return [
63
+ {
64
+ id: 0,
65
+ name: 'Info',
66
+ display: true,
67
+ },
68
+ {
69
+ id: 1,
70
+ name: 'Columns',
71
+ display: true,
72
+ },
73
+ {
74
+ id: 2,
75
+ name: 'Graph',
76
+ display: true,
77
+ },
78
+ {
79
+ id: 3,
80
+ name: 'History',
81
+ display: true,
82
+ },
83
+ {
84
+ id: 4,
85
+ name: 'SQL',
86
+ display: node?.type !== 'dimension' && node?.type !== 'source',
87
+ },
88
+ {
89
+ id: 5,
90
+ name: 'Materializations',
91
+ display: node?.type !== 'source',
92
+ },
93
+ {
94
+ id: 6,
95
+ name: 'Linked Nodes',
96
+ display: node?.type === 'dimension',
97
+ },
98
+ ];
99
+ };
100
+
86
101
  //
87
102
  //
88
103
  let tabToDisplay = null;
@@ -106,6 +121,9 @@ export function NodePage() {
106
121
  case 5:
107
122
  tabToDisplay = <NodeMaterializationTab node={node} djClient={djClient} />;
108
123
  break;
124
+ case 6:
125
+ tabToDisplay = <NodesWithDimension node={node} djClient={djClient} />;
126
+ break;
109
127
  default:
110
128
  tabToDisplay = <NodeInfoTab node={node} />;
111
129
  }
@@ -121,12 +139,15 @@ export function NodePage() {
121
139
  style={{ display: 'inline-block' }}
122
140
  >
123
141
  <span className="card-label fw-bold text-gray-800">
124
- {node?.display_name}
142
+ {node?.display_name}{' '}
143
+ <span className={'node_type__' + node?.type + ' badge node_type'}>
144
+ {node?.type}
145
+ </span>
125
146
  </span>
126
147
  </h3>
127
148
  <ClientCodePopover code={node?.createNodeClientCode} />
128
149
  <div className="align-items-center row">
129
- {TabsJson.map(buildTabs)}
150
+ {tabsList(node).map(buildTabs)}
130
151
  </div>
131
152
  {tabToDisplay}
132
153
  </div>
@@ -9,6 +9,7 @@ import Select from 'react-select';
9
9
  import QueryInfo from '../../components/QueryInfo';
10
10
 
11
11
  export function SQLBuilderPage() {
12
+ const DEFAULT_NUM_ROWS = 100;
12
13
  const djClient = useContext(DJClientContext).DataJunctionAPI;
13
14
  const [stagedMetrics, setStagedMetrics] = useState([]);
14
15
  const [metrics, setMetrics] = useState([]);
@@ -17,12 +18,11 @@ export function SQLBuilderPage() {
17
18
  const [stagedDimensions, setStagedDimensions] = useState([]);
18
19
  const [selectedMetrics, setSelectedMetrics] = useState([]);
19
20
  const [query, setQuery] = useState('');
20
- const [submittedQueryInfo, setSubmittedQueryInfo] = useState(null);
21
+ const [queryInfo, setQueryInfo] = useState({});
21
22
  const [data, setData] = useState(null);
22
23
  const [loadingData, setLoadingData] = useState(false);
23
24
  const [viewData, setViewData] = useState(false);
24
- const [showHelp, setShowHelp] = useState(true);
25
- const [showNumRows, setShowNumRows] = useState(100);
25
+ const [showNumRows, setShowNumRows] = useState(DEFAULT_NUM_ROWS);
26
26
  const [displayedRows, setDisplayedRows] = useState(<></>);
27
27
  const numRowsOptions = [
28
28
  {
@@ -46,20 +46,39 @@ export function SQLBuilderPage() {
46
46
  // Get data for the current selection of metrics and dimensions
47
47
  const getData = () => {
48
48
  setLoadingData(true);
49
+ setQueryInfo({});
49
50
  const fetchData = async () => {
50
- setData(null);
51
- const queryInfo = await djClient.data(
52
- selectedMetrics,
53
- selectedDimensions,
54
- );
55
- setLoadingData(false);
56
- setSubmittedQueryInfo(queryInfo);
57
- queryInfo.numRows = 0;
58
- if (queryInfo.results && queryInfo.results?.length) {
59
- setData(queryInfo.results);
60
- queryInfo.numRows = queryInfo.results[0].rows.length;
61
- setViewData(true);
62
- setShowNumRows(100);
51
+ if (process.env.REACT_USE_SSE) {
52
+ const sse = await djClient.stream(selectedMetrics, selectedDimensions);
53
+ sse.onmessage = e => {
54
+ const messageData = JSON.parse(JSON.parse(e.data));
55
+ setQueryInfo(messageData);
56
+ if (messageData.results) {
57
+ setLoadingData(false);
58
+ setData(messageData.results);
59
+ messageData.numRows = messageData.results?.length
60
+ ? messageData.results[0].rows.length
61
+ : [];
62
+ setViewData(true);
63
+ setShowNumRows(DEFAULT_NUM_ROWS);
64
+ }
65
+ };
66
+ sse.onerror = () => sse.close();
67
+ } else {
68
+ const response = await djClient.data(
69
+ selectedMetrics,
70
+ selectedDimensions,
71
+ );
72
+ setQueryInfo(response);
73
+ if (response.results) {
74
+ setLoadingData(false);
75
+ setData(response.results);
76
+ response.numRows = response.results?.length
77
+ ? response.results[0].rows.length
78
+ : [];
79
+ setViewData(true);
80
+ setShowNumRows(DEFAULT_NUM_ROWS);
81
+ }
63
82
  }
64
83
  };
65
84
  fetchData().catch(console.error);
@@ -69,25 +88,7 @@ export function SQLBuilderPage() {
69
88
  setQuery('');
70
89
  setData(null);
71
90
  setViewData(false);
72
- };
73
- const handleMetricSelect = event => {
74
- const metrics = event.map(m => m.value);
75
- resetView();
76
- setStagedMetrics(metrics);
77
- };
78
-
79
- const handleMetricSelectorClose = () => {
80
- setSelectedMetrics(stagedMetrics);
81
- };
82
-
83
- const handleDimensionSelect = event => {
84
- const dimensions = event.map(d => d.value);
85
- resetView();
86
- setStagedDimensions(dimensions);
87
- };
88
-
89
- const handleDimensionSelectorClose = () => {
90
- setSelectedDimensions(stagedDimensions);
91
+ setQueryInfo({});
91
92
  };
92
93
 
93
94
  // Get metrics
@@ -125,11 +126,9 @@ export function SQLBuilderPage() {
125
126
  const fetchData = async () => {
126
127
  if (selectedMetrics.length && selectedDimensions.length) {
127
128
  const query = await djClient.sqls(selectedMetrics, selectedDimensions);
128
- setShowHelp(false);
129
- setQuery(query);
129
+ setQuery(query.sql);
130
130
  } else {
131
131
  resetView();
132
- setShowHelp(true);
133
132
  }
134
133
  };
135
134
  fetchData().catch(console.error);
@@ -139,7 +138,7 @@ export function SQLBuilderPage() {
139
138
  useEffect(() => {
140
139
  if (data) {
141
140
  setDisplayedRows(
142
- data[0].rows.slice(0, showNumRows).map((rowData, index) => (
141
+ data[0]?.rows.slice(0, showNumRows).map((rowData, index) => (
143
142
  <tr key={`data-row:${index}`}>
144
143
  {rowData.map(rowValue => (
145
144
  <td key={rowValue}>{rowValue}</td>
@@ -167,13 +166,26 @@ export function SQLBuilderPage() {
167
166
  <Select
168
167
  name="metrics"
169
168
  options={metrics}
169
+ isDisabled={
170
+ selectedMetrics.length && selectedDimensions.length
171
+ ? true
172
+ : false
173
+ }
170
174
  noOptionsMessage={() => 'No metrics found.'}
171
175
  placeholder={`${metrics.length} Available Metrics`}
172
176
  isMulti
173
177
  isClearable
174
178
  closeMenuOnSelect={false}
175
- onChange={handleMetricSelect}
176
- onMenuClose={handleMetricSelectorClose}
179
+ onChange={e => {
180
+ setSelectedDimensions([]);
181
+ resetView();
182
+ setStagedMetrics(e.map(m => m.value));
183
+ }}
184
+ onMenuClose={() => {
185
+ resetView();
186
+ setSelectedDimensions([]);
187
+ setSelectedMetrics(stagedMetrics);
188
+ }}
177
189
  />
178
190
  <h4>Group By</h4>
179
191
  <Select
@@ -187,38 +199,17 @@ export function SQLBuilderPage() {
187
199
  isMulti
188
200
  isClearable
189
201
  closeMenuOnSelect={false}
190
- onChange={handleDimensionSelect}
191
- onMenuClose={handleDimensionSelectorClose}
192
- />
193
- <h4>Filter By</h4>
194
- <Select
195
- name="filters"
196
- className="filters"
197
- formatOptionLabel={formatOptionLabel}
198
- options={commonDimensionsList}
199
- noOptionsMessage={() =>
200
- 'No shared dimensions found. Try selecting different metrics.'
201
- }
202
- placeholder={`${commonDimensionsList.length} Shared Dimensions`}
203
- isClearable
204
- closeMenuOnSelect={true}
205
- // onChange={handleDimensionSelect}
206
- // onMenuClose={handleDimensionSelectorClose}
207
- />
208
- <Select
209
- name="filters"
210
- className="filters-operator"
211
- // formatOptionLabel={formatOptionLabel}
212
- options={[{value: '=', label: '='}, {value: '>', label: '>'}, {value: '>', label: '>'}]}
213
- placeholder={`Operator`}
214
- closeMenuOnSelect={true}
215
- // onChange={handleDimensionSelect}
216
- // onMenuClose={handleDimensionSelectorClose}
202
+ onChange={e => {
203
+ resetView();
204
+ setStagedDimensions(e.map(d => d.value));
205
+ }}
206
+ onMenuClose={() => {
207
+ setSelectedDimensions(stagedDimensions);
208
+ }}
217
209
  />
218
- <input value={`Value`}></input>
219
210
  </div>
220
211
  <div className="card-header">
221
- {showHelp ? (
212
+ {!viewData && !query ? (
222
213
  <div className="card-light-shadow">
223
214
  <h6>Using the SQL Builder</h6>
224
215
  <p>
@@ -277,7 +268,7 @@ export function SQLBuilderPage() {
277
268
  <span style={{ display: 'inline-block' }}>
278
269
  <Select
279
270
  name="num-rows"
280
- defaultValue={numRowsOptions[1]}
271
+ defaultValue={numRowsOptions[0]}
281
272
  options={numRowsOptions}
282
273
  onChange={e => setShowNumRows(e.value)}
283
274
  />
@@ -298,19 +289,11 @@ export function SQLBuilderPage() {
298
289
  ) : (
299
290
  <></>
300
291
  )}
301
- {submittedQueryInfo ? <QueryInfo {...submittedQueryInfo} /> : <></>}
292
+ {queryInfo && queryInfo.id ? <QueryInfo {...queryInfo} /> : <></>}
302
293
  <div>
303
- {/*{query.dialect}*/}
304
294
  {query && !viewData ? (
305
295
  <SyntaxHighlighter language="sql" style={foundation}>
306
- {format(query.sql, {
307
- language: 'spark',
308
- tabWidth: 2,
309
- keywordCase: 'upper',
310
- denseOperators: true,
311
- logicalOperatorNewline: 'before',
312
- expressionWidth: 10,
313
- })}
296
+ {query}
314
297
  </SyntaxHighlighter>
315
298
  ) : (
316
299
  ''
@@ -321,7 +304,7 @@ export function SQLBuilderPage() {
321
304
  <table className="card-inner-table table">
322
305
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
323
306
  <tr>
324
- {data[0].columns.map(columnName => (
307
+ {data[0]?.columns.map(columnName => (
325
308
  <th key={columnName.name}>{columnName.name}</th>
326
309
  ))}
327
310
  </tr>
@@ -43,7 +43,7 @@ export const DataJunctionAPI = {
43
43
 
44
44
  clientCode: async function (name) {
45
45
  const data = await (
46
- await fetch(DJ_URL + '/client/python/new_node/' + name)
46
+ await fetch(DJ_URL + '/datajunction-clients/python/new_node/' + name)
47
47
  ).json();
48
48
  return data;
49
49
  },
@@ -70,11 +70,9 @@ export const DataJunctionAPI = {
70
70
  const data = await (
71
71
  await fetch(
72
72
  DJ_URL +
73
- '/history/' +
74
- type +
75
- '/' +
73
+ '/history?node=' +
76
74
  name +
77
- `/?offset=${offset ? offset : 0}&limit=${limit ? limit : 100}`,
75
+ `&offset=${offset ? offset : 0}&limit=${limit ? limit : 100}`,
78
76
  )
79
77
  ).json();
80
78
  return data;
@@ -108,6 +106,13 @@ export const DataJunctionAPI = {
108
106
  return data;
109
107
  },
110
108
 
109
+ nodesWithDimension: async function (name) {
110
+ const data = await (
111
+ await fetch(DJ_URL + '/dimensions/' + name + '/nodes/')
112
+ ).json();
113
+ return data;
114
+ },
115
+
111
116
  materializations: async function (node) {
112
117
  const data = await (
113
118
  await fetch(DJ_URL + `/nodes/${node}/materializations/`)
@@ -118,7 +123,7 @@ export const DataJunctionAPI = {
118
123
  materialization.clientCode = await (
119
124
  await fetch(
120
125
  DJ_URL +
121
- `/client/python/add_materialization/${node}/${materialization.name}`,
126
+ `/datajunction-clients/python/add_materialization/${node}/${materialization.name}`,
122
127
  )
123
128
  ).json();
124
129
  return materialization;
@@ -129,12 +134,14 @@ export const DataJunctionAPI = {
129
134
  columns: async function (node) {
130
135
  return await Promise.all(
131
136
  node.columns.map(async col => {
132
- col.clientCode = await (
133
- await fetch(
134
- DJ_URL +
135
- `/client/python/link_dimension/${node.name}/${col.name}/${col.dimension?.name}`,
136
- )
137
- ).json();
137
+ if (col.dimension !== null) {
138
+ col.clientCode = await (
139
+ await fetch(
140
+ DJ_URL +
141
+ `/datajunction-clients/python/link_dimension/${node.name}/${col.name}/${col.dimension?.name}`,
142
+ )
143
+ ).json();
144
+ }
138
145
  return col;
139
146
  }),
140
147
  );
@@ -153,11 +160,20 @@ export const DataJunctionAPI = {
153
160
  metricSelection.map(metric => params.append('metrics', metric));
154
161
  dimensionSelection.map(dimension => params.append('dimensions', dimension));
155
162
  const data = await (
156
- await fetch(DJ_URL + '/data/?' + params + '&limit=100&async_=true')
163
+ await fetch(DJ_URL + '/data/?' + params + '&limit=10000')
157
164
  ).json();
158
165
  return data;
159
166
  },
160
167
 
168
+ stream: async function (metricSelection, dimensionSelection) {
169
+ const params = new URLSearchParams();
170
+ metricSelection.map(metric => params.append('metrics', metric));
171
+ dimensionSelection.map(dimension => params.append('dimensions', dimension));
172
+ return new EventSource(
173
+ DJ_URL + '/stream/?' + params + '&limit=10000&async_=true',
174
+ );
175
+ },
176
+
161
177
  lineage: async function (node) {},
162
178
 
163
179
  compiledSql: async function (node) {
@@ -265,15 +265,18 @@ tr {
265
265
  left: 0;
266
266
  }
267
267
 
268
- .table-responsive {
268
+ .table-responsive,
269
+ .table-vertical {
269
270
  overflow-x: auto;
270
271
  -webkit-overflow-scrolling: touch;
271
272
  display: grid;
272
-
273
273
  grid-template-columns: auto auto auto;
274
274
  grid-gap: 8px;
275
275
  padding: 8px;
276
276
  }
277
+ .table-vertical {
278
+ display: contents;
279
+ }
277
280
  .table > thead {
278
281
  vertical-align: bottom;
279
282
  }
@@ -430,12 +433,12 @@ tbody th {
430
433
  color: #00b368;
431
434
  }
432
435
 
433
- .history_type__deactvate {
434
- background-color: #a8a8a8 !important;
435
- color: #855e5e;
436
+ .history_type__delete {
437
+ background-color: #ffc8c8 !important;
438
+ color: #9b5a5a;
436
439
  }
437
440
 
438
- .history_type__activate {
441
+ .history_type__restore {
439
442
  background-color: #ccf7e5 !important;
440
443
  color: #00b368;
441
444
  }
@@ -456,8 +459,13 @@ tbody th {
456
459
  }
457
460
 
458
461
  .history_type__set_attribute {
459
- background-color: #a8a8a8 !important;
460
- color: #855e5e;
462
+ background-color: #e6f5ff !important;
463
+ color: #487c8c;
464
+ }
465
+
466
+ .history_type__status_change {
467
+ background-color: #ffe9db !important;
468
+ color: #573d3d;
461
469
  }
462
470
 
463
471
  .partition_value {
@@ -865,13 +873,3 @@ pre {
865
873
  .inactive {
866
874
  display: none;
867
875
  }
868
-
869
- .filters {
870
- width: 500px;
871
- display: inline-table;
872
- }
873
-
874
- .filters-operator {
875
- width: 200px;
876
- display: inline-table;
877
- }