datajunction-ui 0.0.1-a46 → 0.0.1-a46.dev3

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.
@@ -1,10 +1,9 @@
1
1
  import { useEffect, useState } from 'react';
2
+ import ClientCodePopover from './ClientCodePopover';
2
3
  import TableIcon from '../../icons/TableIcon';
3
4
  import AddMaterializationPopover from './AddMaterializationPopover';
4
5
  import * as React from 'react';
5
6
  import AddBackfillPopover from './AddBackfillPopover';
6
- import { labelize } from '../../../utils/form';
7
- import NodeMaterializationDelete from '../../components/NodeMaterializationDelete';
8
7
 
9
8
  const cronstrue = require('cronstrue');
10
9
 
@@ -19,6 +18,17 @@ export default function NodeMaterializationTab({ node, djClient }) {
19
18
  };
20
19
  fetchData().catch(console.error);
21
20
  }, [djClient, node]);
21
+ //
22
+ // const rangePartition = partition => {
23
+ // return (
24
+ // <div>
25
+ // <span className="badge partition_value">
26
+ // <span className="badge partition_value">{partition.range[0]}</span>to
27
+ // <span className="badge partition_value">{partition.range[1]}</span>
28
+ // </span>
29
+ // </div>
30
+ // );
31
+ // };
22
32
 
23
33
  const partitionColumnsMap = node
24
34
  ? Object.fromEntries(
@@ -37,195 +47,116 @@ export default function NodeMaterializationTab({ node, djClient }) {
37
47
 
38
48
  const materializationRows = materializations => {
39
49
  return materializations.map(materialization => (
40
- <>
41
- <div className="tr">
42
- <div key={materialization.name} style={{ fontSize: 'large' }}>
43
- <div
44
- className="text-start node_name td"
45
- style={{ fontWeight: '600' }}
46
- >
47
- {materialization.job
48
- ?.replace('MaterializationJob', '')
49
- .match(/[A-Z][a-z]+/g)
50
- .join(' ')}
51
- </div>
52
- <div className="td">
53
- <NodeMaterializationDelete
54
- nodeName={node.name}
55
- materializationName={materialization.name}
56
- />
57
- </div>
58
- <div className="td">
59
- <span className={`badge cron`}>{materialization.schedule}</span>
60
- <div className={`cron-description`}>{cron(materialization)} </div>
61
- </div>
62
- <div className="td">
63
- <span className={`badge strategy`}>
64
- {labelize(materialization.strategy)}
65
- </span>
50
+ <tr key={materialization.name}>
51
+ <td className="text-start node_name">
52
+ <span className={`badge cron`}>{materialization.schedule}</span>
53
+ <div className={`cron-description`}>{cron(materialization)} </div>
54
+ </td>
55
+ <td>
56
+ {materialization.job?.replace('MaterializationJob', '').toUpperCase()}
57
+ </td>
58
+ <td>{materialization.strategy?.toUpperCase()}</td>
59
+ <td>
60
+ {node.columns
61
+ .filter(col => col.partition !== null)
62
+ .map(column => {
63
+ return (
64
+ <div className="partition__full" key={column.name}>
65
+ <div className="partition__header">{column.display_name}</div>
66
+ <div className="partition__body">
67
+ <code>{column.name}</code>
68
+ <span className="badge partition_value">
69
+ {column.partition.type_}
70
+ </span>
71
+ </div>
72
+ </div>
73
+ );
74
+ })}
75
+ </td>
76
+ <td>
77
+ {materialization.output_tables.map(table => (
78
+ <div className={`table__full`} key={table}>
79
+ <div className="table__header">
80
+ <TableIcon />{' '}
81
+ <span className={`entity-info`}>
82
+ {table.split('.')[0] + '.' + table.split('.')[1]}
83
+ </span>
84
+ </div>
85
+ <div className={`table__body upstream_tables`}>
86
+ {table.split('.')[2]}
87
+ </div>
66
88
  </div>
67
- </div>
68
- </div>
69
- <div style={{ display: 'table-row' }}>
70
- <div style={{ display: 'inline-flex' }}>
71
- <ul className="backfills">
72
- <li className="backfill">
73
- <div className="backfills_header">Output Tables</div>{' '}
74
- {materialization.output_tables.map(table => (
75
- <div className={`table__full`} key={table}>
76
- <div className="table__header">
77
- <TableIcon />{' '}
78
- <span className={`entity-info`}>
79
- {table.split('.')[0] + '.' + table.split('.')[1]}
80
- </span>
81
- </div>
82
- <div className={`table__body upstream_tables`}>
83
- {table.split('.')[2]}
84
- </div>
89
+ ))}
90
+ </td>
91
+ {materializations[0].strategy === 'incremental_time' ? (
92
+ <td>
93
+ {materialization.backfills.map(backfill => (
94
+ <a href={backfill.urls[0]} className="partitionLink">
95
+ <div
96
+ className="partition__full"
97
+ key={backfill.spec.column_name}
98
+ >
99
+ <div className="partition__header">
100
+ {partitionColumnsMap[backfill.spec.column_name]}
85
101
  </div>
86
- ))}
87
- </li>
88
- </ul>
89
- </div>
90
-
91
- <div style={{ display: 'inline-flex' }}>
92
- <ul className="backfills">
93
- <li>
94
- <div className="backfills_header">Workflows</div>{' '}
95
- <ul>
96
- {materialization.urls.map((url, idx) => (
97
- <li style={{ listStyle: 'none' }} key={idx}>
98
- <div
99
- className="partitionLink"
100
- style={{ fontSize: 'revert' }}
101
- >
102
- <a
103
- href={url}
104
- key={`url-${idx}`}
105
- className=""
106
- target="blank"
107
- >
108
- {idx === 0 ? 'main' : 'backfill'}
109
- </a>
110
- </div>
111
- </li>
112
- ))}
113
- </ul>
114
- </li>
115
- </ul>
116
- </div>
117
-
118
- <div style={{ display: 'inline-flex' }}>
119
- <ul className="backfills">
120
- <li className="backfill">
121
- <details open>
122
- <summary>
123
- <span className="backfills_header">Backfills</span>{' '}
124
- </summary>
125
- {materialization.strategy === 'incremental_time' ? (
126
- <ul>
127
- <li>
128
- <AddBackfillPopover
129
- node={node}
130
- materialization={materialization}
131
- />
132
- </li>
133
- {materialization.backfills.map(backfill => (
134
- <li className="backfill">
135
- <div className="partitionLink">
136
- <a href={backfill.urls[0]}>
137
- {backfill.spec.map(partition => {
138
- const partitionBody =
139
- 'range' in partition &&
140
- partition['range'] !== null ? (
141
- <>
142
- <span className="badge partition_value">
143
- {partition.range[0]}
144
- </span>
145
- to
146
- <span className="badge partition_value">
147
- {partition.range[1]}
148
- </span>
149
- </>
150
- ) : (
151
- <span className="badge partition_value">
152
- {partition.values.join(', ')}
153
- </span>
154
- );
155
- return (
156
- <>
157
- <div>
158
- {
159
- partitionColumnsMap[
160
- partition.column_name.replaceAll(
161
- '_DOT_',
162
- '.',
163
- )
164
- ]
165
- }{' '}
166
- {partitionBody}
167
- </div>
168
- </>
169
- );
170
- })}
171
- </a>
172
- </div>
173
- </li>
174
- ))}
175
- </ul>
176
- ) : (
177
- <ul>
178
- <li>N/A</li>
179
- </ul>
180
- )}
181
- </details>
182
- </li>
183
- </ul>
184
- </div>
185
- <div className="td">
186
- <ul className="backfills">
187
- <li className="backfill">
188
- <div className="backfills_header">Partitions</div>{' '}
189
- <ul>
190
- {node.columns
191
- .filter(col => col.partition !== null)
192
- .map(column => {
193
- return (
194
- <li>
195
- <div className="partitionLink">
196
- {column.display_name}
197
- <span className="badge partition_value">
198
- {column.partition.type_}
199
- </span>
200
- </div>
201
- </li>
202
- );
203
- })}
204
- </ul>
205
- </li>
206
- </ul>
207
- </div>
208
- </div>
209
- </>
102
+ <div className="partition__body">
103
+ <span className="badge partition_value">
104
+ {backfill.spec.range[0]}
105
+ </span>
106
+ to
107
+ <span className="badge partition_value">
108
+ {backfill.spec.range[1]}
109
+ </span>
110
+ </div>
111
+ </div>
112
+ </a>
113
+ ))}
114
+ <AddBackfillPopover node={node} materialization={materialization} />
115
+ </td>
116
+ ) : (
117
+ <></>
118
+ )}
119
+ <td>
120
+ {materialization.urls.map((url, idx) => (
121
+ <a href={url} key={`url-${idx}`}>
122
+ [{idx + 1}]
123
+ </a>
124
+ ))}
125
+ </td>
126
+ <td>
127
+ <ClientCodePopover code={materialization.clientCode} />
128
+ </td>
129
+ </tr>
210
130
  ));
211
131
  };
212
132
  return (
213
133
  <>
214
- <div
215
- className="table-vertical"
216
- role="table"
217
- aria-label="Materializations"
218
- >
134
+ <div className="table-vertical">
219
135
  <div>
220
136
  <h2>Materializations</h2>
221
137
  {node ? <AddMaterializationPopover node={node} /> : <></>}
222
138
  {materializations.length > 0 ? (
223
- <div
139
+ <table
224
140
  className="card-inner-table table"
225
141
  aria-label="Materializations"
226
142
  aria-hidden="false"
227
143
  >
228
- <div style={{ display: 'table' }}>
144
+ <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
145
+ <tr>
146
+ <th className="text-start">Schedule</th>
147
+ <th>Job Type</th>
148
+ <th>Strategy</th>
149
+ <th>Partitions</th>
150
+ <th>Intended Output Tables</th>
151
+ {materializations[0].strategy === 'incremental_time' ? (
152
+ <th>Backfills</th>
153
+ ) : (
154
+ <></>
155
+ )}
156
+ <th>URLs</th>
157
+ </tr>
158
+ </thead>
159
+ <tbody>
229
160
  {materializationRows(
230
161
  materializations.filter(
231
162
  materialization =>
@@ -235,8 +166,8 @@ export default function NodeMaterializationTab({ node, djClient }) {
235
166
  ),
236
167
  ),
237
168
  )}
238
- </div>
239
- </div>
169
+ </tbody>
170
+ </table>
240
171
  ) : (
241
172
  <div className="message alert" style={{ marginTop: '10px' }}>
242
173
  No materialization workflows configured for this node.
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { render, fireEvent, waitFor } from '@testing-library/react';
2
+ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
3
+ import EditColumnPopover from '../EditColumnPopover';
3
4
  import DJClientContext from '../../../providers/djclient';
4
5
  import AddBackfillPopover from '../AddBackfillPopover';
5
6
  import { mocks } from '../../../../mocks/mockNodes';
@@ -10,17 +11,6 @@ const mockDjClient = {
10
11
  },
11
12
  };
12
13
 
13
- let reloadMock = jest.fn();
14
-
15
- beforeEach(() => {
16
- delete window.location;
17
- window.location = { reload: reloadMock };
18
- });
19
-
20
- afterEach(() => {
21
- reloadMock.mockClear();
22
- });
23
-
24
14
  describe('<AddBackfillPopover />', () => {
25
15
  it('renders correctly and handles form submission', async () => {
26
16
  // Mock onSubmit function
@@ -35,7 +25,7 @@ describe('<AddBackfillPopover />', () => {
35
25
  const { getByLabelText, getByText } = render(
36
26
  <DJClientContext.Provider value={mockDjClient}>
37
27
  <AddBackfillPopover
38
- node={mocks.mockTransformNode}
28
+ node={mocks.mockMetricNode}
39
29
  materialization={mocks.nodeMaterializations}
40
30
  onSubmit={onSubmitMock}
41
31
  />
@@ -29,7 +29,6 @@ describe('<NodePage />', () => {
29
29
  history: jest.fn(),
30
30
  revisions: jest.fn(),
31
31
  materializations: jest.fn(),
32
- materializationInfo: jest.fn(),
33
32
  sql: jest.fn(),
34
33
  cube: jest.fn(),
35
34
  compiledSql: jest.fn(),
@@ -605,17 +604,13 @@ describe('<NodePage />', () => {
605
604
 
606
605
  it('renders the NodeMaterialization tab with materializations correctly', async () => {
607
606
  const djClient = mockDJClient();
608
- djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockTransformNode);
609
- // djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
607
+ djClient.DataJunctionAPI.node.mockReturnValue(mocks.mockMetricNode);
608
+ djClient.DataJunctionAPI.metric.mockReturnValue(mocks.mockMetricNode);
610
609
  djClient.DataJunctionAPI.columns.mockReturnValue(mocks.metricNodeColumns);
611
610
  djClient.DataJunctionAPI.materializations.mockReturnValue(
612
611
  mocks.nodeMaterializations,
613
612
  );
614
613
 
615
- djClient.DataJunctionAPI.materializationInfo.mockReturnValue(
616
- mocks.materializationInfo,
617
- );
618
-
619
614
  const element = (
620
615
  <DJClientContext.Provider value={djClient}>
621
616
  <NodePage />
@@ -623,9 +618,7 @@ describe('<NodePage />', () => {
623
618
  );
624
619
  render(
625
620
  <MemoryRouter
626
- initialEntries={[
627
- '/nodes/default.repair_order_transform/materializations',
628
- ]}
621
+ initialEntries={['/nodes/default.num_repair_orders/materializations']}
629
622
  >
630
623
  <Routes>
631
624
  <Route path="nodes/:name/:tab" element={element} />
@@ -638,10 +631,10 @@ describe('<NodePage />', () => {
638
631
  screen.getByRole('button', { name: 'Materializations' }),
639
632
  );
640
633
  expect(djClient.DataJunctionAPI.node).toHaveBeenCalledWith(
641
- mocks.mockTransformNode.name,
634
+ mocks.mockMetricNode.name,
642
635
  );
643
636
  expect(djClient.DataJunctionAPI.materializations).toHaveBeenCalledWith(
644
- mocks.mockTransformNode.name,
637
+ mocks.mockMetricNode.name,
645
638
  );
646
639
 
647
640
  expect(