datajunction-ui 0.0.43 → 0.0.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datajunction-ui",
3
- "version": "0.0.43",
3
+ "version": "0.0.44",
4
4
  "description": "DataJunction UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -4,7 +4,6 @@ import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
4
4
  import sql from 'react-syntax-highlighter/dist/esm/languages/hljs/sql';
5
5
  import NodeStatus from './NodeStatus';
6
6
  import ListGroupItem from '../../components/ListGroupItem';
7
- import ToggleSwitch from '../../components/ToggleSwitch';
8
7
  import DJClientContext from '../../providers/djclient';
9
8
  import { labelize } from '../../../utils/form';
10
9
 
@@ -30,9 +29,6 @@ foundation.hljs['padding'] = '2rem';
30
29
  // }
31
30
 
32
31
  export default function NodeInfoTab({ node }) {
33
- const [compiledSQL, setCompiledSQL] = useState('');
34
- const [checked, setChecked] = useState(false);
35
-
36
32
  // For metrics
37
33
  const [metricInfo, setMetricInfo] = useState(null);
38
34
 
@@ -47,22 +43,6 @@ export default function NodeInfoTab({ node }) {
47
43
  ));
48
44
  const djClient = useContext(DJClientContext).DataJunctionAPI;
49
45
 
50
- useEffect(() => {
51
- const fetchData = async () => {
52
- if (checked === true) {
53
- const data = await djClient.compiledSql(node.name);
54
- if (data.sql) {
55
- setCompiledSQL(data.sql);
56
- } else {
57
- setCompiledSQL(
58
- '/* Ran into an issue while generating compiled SQL */',
59
- );
60
- }
61
- }
62
- };
63
- fetchData().catch(console.error);
64
- }, [node, djClient, checked]);
65
-
66
46
  useEffect(() => {
67
47
  const fetchData = async () => {
68
48
  const metric = await djClient.getMetric(node.name);
@@ -96,9 +76,6 @@ export default function NodeInfoTab({ node }) {
96
76
  }
97
77
  }, [node, djClient]);
98
78
 
99
- function toggle(value) {
100
- return !value;
101
- }
102
79
  const metricsWarning =
103
80
  node?.type === 'metric' &&
104
81
  metricInfo?.incompatible_druid_functions?.length > 0 ? (
@@ -149,7 +126,11 @@ export default function NodeInfoTab({ node }) {
149
126
  )}
150
127
  <div>
151
128
  <h6 className="mb-0 w-100">Aggregate Expression</h6>
152
- <SyntaxHighlighter language="sql" style={foundation}>
129
+ <SyntaxHighlighter
130
+ language="sql"
131
+ style={foundation}
132
+ wrapLongLines={true}
133
+ >
153
134
  {metricInfo?.expression}
154
135
  </SyntaxHighlighter>
155
136
  </div>
@@ -167,18 +148,12 @@ export default function NodeInfoTab({ node }) {
167
148
  }}
168
149
  >
169
150
  <h6 className="mb-0 w-100">Query</h6>
170
- {['metric', 'dimension', 'transform'].indexOf(node?.type) > -1 ? (
171
- <ToggleSwitch
172
- id="toggleSwitch"
173
- checked={checked}
174
- onChange={() => setChecked(toggle)}
175
- toggleName="Show Compiled SQL"
176
- />
177
- ) : (
178
- <></>
179
- )}
180
- <SyntaxHighlighter language="sql" style={foundation}>
181
- {checked ? compiledSQL : node?.query}
151
+ <SyntaxHighlighter
152
+ language="sql"
153
+ style={foundation}
154
+ wrapLongLines={true}
155
+ >
156
+ {node?.query}
182
157
  </SyntaxHighlighter>
183
158
  </div>
184
159
  </div>
@@ -218,7 +193,26 @@ export default function NodeInfoTab({ node }) {
218
193
  node?.type === 'metric' ? (
219
194
  <div className="list-group-item d-flex">
220
195
  <div className="d-flex gap-2 w-100 py-3">
221
- <div>
196
+ <div style={{ marginRight: '2rem' }}>
197
+ <h6 className="mb-0 w-100">Output Type</h6>
198
+ <p
199
+ className="mb-0 opacity-75"
200
+ role="dialog"
201
+ aria-hidden="false"
202
+ aria-label="OutputType"
203
+ >
204
+ <code
205
+ style={{
206
+ background: '#f5f5f5',
207
+ padding: '2px 6px',
208
+ borderRadius: '3px',
209
+ }}
210
+ >
211
+ {node?.columns?.[0]?.type || 'Unknown'}
212
+ </code>
213
+ </p>
214
+ </div>
215
+ <div style={{ marginRight: '2rem' }}>
222
216
  <h6 className="mb-0 w-100">Direction</h6>
223
217
  <p
224
218
  className="mb-0 opacity-75"
@@ -275,7 +269,11 @@ export default function NodeInfoTab({ node }) {
275
269
  }}
276
270
  >
277
271
  <h6 className="mb-0 w-100">Custom Metadata</h6>
278
- <SyntaxHighlighter language="json" style={foundation}>
272
+ <SyntaxHighlighter
273
+ language="json"
274
+ style={foundation}
275
+ wrapLongLines={true}
276
+ >
279
277
  {JSON.stringify(node.custom_metadata, null, 2)}
280
278
  </SyntaxHighlighter>
281
279
  </div>
@@ -331,12 +329,12 @@ export default function NodeInfoTab({ node }) {
331
329
  <a href={`/nodes/${node?.name}`}>{dim}</a>
332
330
  </span>
333
331
  ))
334
- : node?.required_dimensions?.map((dim, idx) => (
332
+ : metricInfo?.required_dimensions?.map((dim, idx) => (
335
333
  <span
336
334
  key={`rd-${idx}`}
337
335
  className="rounded-pill badge bg-secondary-soft PrimaryKey"
338
336
  >
339
- <a href={`/nodes/${node?.upstream_node}`}>{dim.name}</a>
337
+ <a href={`/nodes/${metricInfo?.upstream_node}`}>{dim.name}</a>
340
338
  </span>
341
339
  ))}
342
340
  </p>
@@ -591,32 +591,6 @@ describe('<NodePage />', () => {
591
591
  });
592
592
  });
593
593
 
594
- it('renders compiled sql correctly', async () => {
595
- const djClient = mockDJClient();
596
- djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockTransformNode);
597
- djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
598
- djClient.DataJunctionAPI.compiledSql.mockReturnValue('select 1');
599
-
600
- const element = (
601
- <DJClientContext.Provider value={djClient}>
602
- <NodePage />
603
- </DJClientContext.Provider>
604
- );
605
- render(
606
- <MemoryRouter initialEntries={[`/nodes/${mocks.mockTransformNode.name}`]}>
607
- <Routes>
608
- <Route path="nodes/:name" element={element} />
609
- </Routes>
610
- </MemoryRouter>,
611
- );
612
- await waitFor(() => {
613
- fireEvent.click(screen.getByRole('checkbox', { name: 'ToggleSwitch' }));
614
- expect(djClient.DataJunctionAPI.compiledSql).toHaveBeenCalledWith(
615
- mocks.mockTransformNode.name,
616
- );
617
- });
618
- });
619
-
620
594
  it('renders an empty NodeMaterialization tab correctly', async () => {
621
595
  const djClient = mockDJClient();
622
596
  djClient.DataJunctionAPI.node.mockResolvedValue(mocks.mockMetricNode);
@@ -708,113 +682,6 @@ describe('<NodePage />', () => {
708
682
  });
709
683
  });
710
684
 
711
- it('renders the NodeValidate tab', async () => {
712
- const djClient = mockDJClient();
713
- window.scrollTo = jest.fn();
714
- djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
715
- djClient.DataJunctionAPI.nodeDimensions.mockReturnValue([]);
716
- djClient.DataJunctionAPI.getMetric.mockReturnValue(
717
- mocks.mockMetricNodeJson,
718
- );
719
- djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
720
- djClient.DataJunctionAPI.sql.mockReturnValue({
721
- sql: 'SELECT * FROM testNode',
722
- });
723
- const streamNodeData = {
724
- onmessage: jest.fn(),
725
- onerror: jest.fn(),
726
- close: jest.fn(),
727
- };
728
- djClient.DataJunctionAPI.streamNodeData.mockResolvedValue(streamNodeData);
729
- djClient.DataJunctionAPI.streamNodeData.mockResolvedValueOnce({
730
- state: 'FINISHED',
731
- results: [
732
- {
733
- columns: [{ name: 'column1' }, { name: 'column2' }],
734
- rows: [
735
- [1, 'value1'],
736
- [2, 'value2'],
737
- ],
738
- },
739
- ],
740
- });
741
-
742
- const element = (
743
- <DJClientContext.Provider value={djClient}>
744
- <NodePage />
745
- </DJClientContext.Provider>
746
- );
747
- render(
748
- <MemoryRouter
749
- initialEntries={['/nodes/default.num_repair_orders/validate']}
750
- >
751
- <Routes>
752
- <Route path="nodes/:name/:tab" element={element} />
753
- </Routes>
754
- </MemoryRouter>,
755
- );
756
-
757
- await waitFor(() => {
758
- expect(screen.getByText('Group By')).toBeInTheDocument();
759
- expect(screen.getByText('Add Filters')).toBeInTheDocument();
760
- expect(screen.getByText('Generated Query')).toBeInTheDocument();
761
- expect(screen.getByText('Results')).toBeInTheDocument();
762
- });
763
- // Click on the 'Validate' tab
764
- fireEvent.click(screen.getByRole('button', { name: '► Validate' }));
765
-
766
- await waitFor(() => {
767
- expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
768
- mocks.mockMetricNode.name,
769
- );
770
- expect(djClient.DataJunctionAPI.sql).toHaveBeenCalledWith(
771
- mocks.mockMetricNode.name,
772
- { dimensions: [], filters: [] },
773
- );
774
- expect(djClient.DataJunctionAPI.nodeDimensions).toHaveBeenCalledWith(
775
- mocks.mockMetricNode.name,
776
- );
777
- });
778
-
779
- // Click on 'Run' to run the node query
780
- const runButton = screen.getByText('► Run');
781
- fireEvent.click(runButton);
782
-
783
- await waitFor(() => {
784
- expect(djClient.DataJunctionAPI.streamNodeData).toHaveBeenCalledWith(
785
- mocks.mockMetricNode.name,
786
- { dimensions: [], filters: [] },
787
- );
788
- expect(streamNodeData.onmessage).toBeDefined();
789
- expect(streamNodeData.onerror).toBeDefined();
790
- });
791
-
792
- const infoTab = screen.getByRole('button', { name: 'QueryInfo' });
793
- const resultsTab = screen.getByText('Results');
794
-
795
- // Initially, the Results tab should be active
796
- expect(resultsTab).toHaveClass('active');
797
- expect(infoTab).not.toHaveClass('active');
798
-
799
- // Click on the Info tab first
800
- fireEvent.click(infoTab);
801
-
802
- await waitFor(() => {
803
- // Now, the Info tab should be active
804
- expect(infoTab).toHaveClass('active');
805
- expect(resultsTab).not.toHaveClass('active');
806
- });
807
-
808
- // Click on the Results tab
809
- fireEvent.click(resultsTab);
810
-
811
- await waitFor(() => {
812
- // Now, the Results tab should be active again
813
- expect(resultsTab).toHaveClass('active');
814
- expect(infoTab).not.toHaveClass('active');
815
- });
816
- });
817
-
818
685
  it('renders a NodeColumnLineage tab correctly', async () => {
819
686
  const djClient = mockDJClient();
820
687
  djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
@@ -4,15 +4,17 @@ exports[`<NodePage /> renders the NodeInfo tab correctly for a metric node 1`] =
4
4
  HTMLCollection [
5
5
  <code
6
6
  class="language-sql"
7
- style="white-space: pre;"
7
+ style="white-space: pre-wrap;"
8
8
  >
9
- <span
10
- class="hljs-built_in"
11
- >
12
- count
13
- </span>
14
9
  <span>
15
- (repair_order_id)
10
+ <span
11
+ class="hljs-built_in"
12
+ >
13
+ count
14
+ </span>
15
+ <span>
16
+ (repair_order_id)
17
+ </span>
16
18
  </span>
17
19
  </code>,
18
20
  ]
@@ -9,7 +9,6 @@ import NodeGraphTab from './NodeGraphTab';
9
9
  import NodeHistory from './NodeHistory';
10
10
  import NotebookDownload from './NotebookDownload';
11
11
  import DJClientContext from '../../providers/djclient';
12
- import NodeValidateTab from './NodeValidateTab';
13
12
  import NodeMaterializationTab from './NodeMaterializationTab';
14
13
  import NodePreAggregationsTab from './NodePreAggregationsTab';
15
14
  import ClientCodePopover from './ClientCodePopover';
@@ -35,6 +34,11 @@ export function NodePage() {
35
34
  const [node, setNode] = useState(null);
36
35
 
37
36
  const onClickTab = id => () => {
37
+ // Preview tab redirects to Query Planner instead of showing content
38
+ if (id === 'preview') {
39
+ navigate(`/planner?metrics=${encodeURIComponent(name)}`);
40
+ return;
41
+ }
38
42
  navigate(`/nodes/${name}/${id}`);
39
43
  setState({ selectedTab: id });
40
44
  };
@@ -74,7 +78,7 @@ export function NodePage() {
74
78
  {
75
79
  id: 'columns',
76
80
  name: 'Columns',
77
- display: true,
81
+ display: node?.type !== 'metric',
78
82
  },
79
83
  {
80
84
  id: 'graph',
@@ -86,15 +90,10 @@ export function NodePage() {
86
90
  name: 'History',
87
91
  display: true,
88
92
  },
89
- {
90
- id: 'validate',
91
- name: '► Validate',
92
- display: node?.type !== 'source',
93
- },
94
93
  {
95
94
  id: 'materializations',
96
95
  name: 'Materializations',
97
- display: node?.type !== 'source',
96
+ display: node?.type !== 'source' && node?.type !== 'metric',
98
97
  },
99
98
  {
100
99
  id: 'linked',
@@ -111,6 +110,11 @@ export function NodePage() {
111
110
  name: 'Dependencies',
112
111
  display: node?.type !== 'cube',
113
112
  },
113
+ {
114
+ id: 'preview',
115
+ name: 'Preview →',
116
+ display: node?.type === 'metric',
117
+ },
114
118
  ];
115
119
  };
116
120
  let tabToDisplay = null;
@@ -128,9 +132,6 @@ export function NodePage() {
128
132
  case 'history':
129
133
  tabToDisplay = <NodeHistory node={node} djClient={djClient} />;
130
134
  break;
131
- case 'validate':
132
- tabToDisplay = <NodeValidateTab node={node} djClient={djClient} />;
133
- break;
134
135
  case 'materializations':
135
136
  // Cube nodes use cube-specific materialization tab
136
137
  // Other nodes (transform, metric, dimension) use pre-aggregations tab