datajunction-ui 0.0.1-a36 → 0.0.1-a37

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.1-a36",
3
+ "version": "0.0.1a37",
4
4
  "description": "DataJunction Metrics Platform UI",
5
5
  "module": "src/index.tsx",
6
6
  "repository": {
@@ -113,7 +113,7 @@ describe('AddEditNodePage submission succeeded', () => {
113
113
  'Number of repair orders!!!',
114
114
  'SELECT count(repair_order_id) default_DOT_num_repair_orders FROM default.repair_orders',
115
115
  'published',
116
- ['repair_order_id', 'country'],
116
+ null,
117
117
  'neutral',
118
118
  'unitless',
119
119
  );
@@ -126,9 +126,6 @@ describe('AddEditNodePage submission succeeded', () => {
126
126
  expect(mockDjClient.DataJunctionAPI.listMetricMetadata).toBeCalledTimes(
127
127
  1,
128
128
  );
129
- expect(
130
- await screen.getByDisplayValue('repair_order_id, country'),
131
- ).toBeInTheDocument();
132
129
  expect(
133
130
  await screen.getByText(/Successfully updated metric node/),
134
131
  ).toBeInTheDocument();
@@ -202,13 +202,18 @@ export function AddEditNodePage() {
202
202
  'mode',
203
203
  'tags',
204
204
  ];
205
+ const primaryKey = data.columns
206
+ .filter(
207
+ col =>
208
+ col.attributes &&
209
+ col.attributes.filter(
210
+ attr => attr.attribute_type.name === 'primary_key',
211
+ ).length > 0,
212
+ )
213
+ .map(col => col.name);
205
214
  fields.forEach(field => {
206
- if (
207
- field === 'primary_key' &&
208
- data[field] !== undefined &&
209
- Array.isArray(data[field])
210
- ) {
211
- data[field] = data[field].join(', ');
215
+ if (field === 'primary_key') {
216
+ setFieldValue(field, primaryKey.join(', '));
212
217
  } else {
213
218
  setFieldValue(field, data[field] || '', false);
214
219
  }
@@ -23,7 +23,7 @@ export const MetricsSelect = ({ cube }) => {
23
23
  // Get metrics
24
24
  useEffect(() => {
25
25
  const fetchData = async () => {
26
- if (cube) {
26
+ if (cube && cube !== []) {
27
27
  const cubeMetrics = cube?.cube_elements
28
28
  .filter(element => element.type === 'metric')
29
29
  .map(metric => {
@@ -10,6 +10,10 @@ const mockDjClient = {
10
10
  createCube: jest.fn(),
11
11
  namespaces: jest.fn(),
12
12
  cube: jest.fn(),
13
+ node: jest.fn(),
14
+ listTags: jest.fn(),
15
+ tagsNode: jest.fn(),
16
+ patchCube: jest.fn(),
13
17
  };
14
18
 
15
19
  const mockMetrics = [
@@ -202,14 +206,12 @@ describe('CubeBuilderPage', () => {
202
206
  mockDjClient.createCube.mockResolvedValue({ status: 201, json: {} });
203
207
  mockDjClient.namespaces.mockResolvedValue(['default']);
204
208
  mockDjClient.cube.mockResolvedValue(mockCube);
209
+ mockDjClient.node.mockResolvedValue(mockCube);
210
+ mockDjClient.listTags.mockResolvedValue([]);
211
+ mockDjClient.tagsNode.mockResolvedValue([]);
212
+ mockDjClient.patchCube.mockResolvedValue({ status: 201, json: {} });
205
213
 
206
214
  window.scrollTo = jest.fn();
207
-
208
- render(
209
- <DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
210
- <CubeBuilderPage />
211
- </DJClientContext.Provider>,
212
- );
213
215
  });
214
216
 
215
217
  afterEach(() => {
@@ -217,14 +219,29 @@ describe('CubeBuilderPage', () => {
217
219
  });
218
220
 
219
221
  it('renders without crashing', () => {
222
+ render(
223
+ <DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
224
+ <CubeBuilderPage />
225
+ </DJClientContext.Provider>,
226
+ );
220
227
  expect(screen.getByText('Cube')).toBeInTheDocument();
221
228
  });
222
229
 
223
230
  it('renders the Metrics section', () => {
231
+ render(
232
+ <DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
233
+ <CubeBuilderPage />
234
+ </DJClientContext.Provider>,
235
+ );
224
236
  expect(screen.getByText('Metrics *')).toBeInTheDocument();
225
237
  });
226
238
 
227
239
  it('renders the Dimensions section', () => {
240
+ render(
241
+ <DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
242
+ <CubeBuilderPage />
243
+ </DJClientContext.Provider>,
244
+ );
228
245
  expect(screen.getByText('Dimensions *')).toBeInTheDocument();
229
246
  });
230
247
 
@@ -307,9 +324,11 @@ describe('CubeBuilderPage', () => {
307
324
 
308
325
  const renderEditNode = element => {
309
326
  return render(
310
- <MemoryRouter initialEntries={['/nodes/default.repair_orders_cube/edit']}>
327
+ <MemoryRouter
328
+ initialEntries={['/nodes/default.repair_orders_cube/edit-cube']}
329
+ >
311
330
  <Routes>
312
- <Route path="nodes/:name/edit" element={element} />
331
+ <Route path="nodes/:name/edit-cube" element={element} />
313
332
  </Routes>
314
333
  </MemoryRouter>,
315
334
  );
@@ -321,7 +340,10 @@ describe('CubeBuilderPage', () => {
321
340
  <CubeBuilderPage />
322
341
  </DJClientContext.Provider>,
323
342
  );
324
-
343
+ expect(screen.getAllByText('Edit')[0]).toBeInTheDocument();
344
+ await waitFor(() => {
345
+ expect(mockDjClient.cube).toHaveBeenCalled();
346
+ });
325
347
  await waitFor(() => {
326
348
  expect(mockDjClient.metrics).toHaveBeenCalled();
327
349
  });
@@ -329,7 +351,7 @@ describe('CubeBuilderPage', () => {
329
351
  const selectMetrics = screen.getAllByTestId('select-metrics')[0];
330
352
  expect(selectMetrics).toBeDefined();
331
353
  expect(selectMetrics).not.toBeNull();
332
- expect(screen.getAllByText('3 Available Metrics')[0]).toBeInTheDocument();
354
+ expect(screen.getByText('default.num_repair_orders')).toBeInTheDocument();
333
355
 
334
356
  fireEvent.click(screen.getAllByText('Dimensions *')[0]);
335
357
 
@@ -364,13 +386,18 @@ describe('CubeBuilderPage', () => {
364
386
  fireEvent.click(createCube);
365
387
  });
366
388
  await waitFor(() => {
367
- expect(mockDjClient.createCube).toHaveBeenCalledWith(
368
- '',
369
- '',
370
- '',
389
+ expect(mockDjClient.patchCube).toHaveBeenCalledWith(
390
+ 'default.repair_orders_cube',
391
+ 'Default: Repair Orders Cube',
392
+ 'Repairs cube',
371
393
  'draft',
372
- [],
373
- [],
394
+ ['default.total_repair_cost', 'default.num_repair_orders'],
395
+ [
396
+ 'default.date_dim.day',
397
+ 'default.date_dim.month',
398
+ 'default.date_dim.year',
399
+ 'default.date_dim.dateint',
400
+ ],
374
401
  [],
375
402
  );
376
403
  });
@@ -87,7 +87,7 @@ export function CubeBuilderPage() {
87
87
  );
88
88
  const tagsResponse = await djClient.tagsNode(
89
89
  values.name,
90
- values.tags.map(tag => tag),
90
+ (values.tags || []).map(tag => tag),
91
91
  );
92
92
  if ((status === 200 || status === 201) && tagsResponse.status === 200) {
93
93
  setStatus({
@@ -97,13 +97,20 @@ export default function AddBackfillPopover({
97
97
  {displayMessageAfterSubmit(status)}
98
98
  <h2>Run Backfill</h2>
99
99
  <span data-testid="edit-partition">
100
- <label htmlFor="engine" style={{ paddingBottom: '1rem' }}>
101
- Engine
100
+ <label
101
+ htmlFor="materializationName"
102
+ style={{ paddingBottom: '1rem' }}
103
+ >
104
+ Materialization Name
102
105
  </label>
103
- <Field as="select" name="engine" id="engine" disabled={true}>
104
- <option value={materialization?.engine?.name}>
105
- {materialization?.engine?.name}{' '}
106
- {materialization?.engine?.version}
106
+ <Field
107
+ as="select"
108
+ name="materializationName"
109
+ id="materializationName"
110
+ disabled={true}
111
+ >
112
+ <option value={materialization?.name}>
113
+ {materialization?.name}{' '}
107
114
  </option>
108
115
  </Field>
109
116
  </span>
@@ -2,27 +2,24 @@ import { useContext, useEffect, useRef, useState } from 'react';
2
2
  import * as React from 'react';
3
3
  import DJClientContext from '../../providers/djclient';
4
4
  import { ErrorMessage, Field, Form, Formik } from 'formik';
5
- import { FormikSelect } from '../AddEditNodePage/FormikSelect';
6
- import EditIcon from '../../icons/EditIcon';
7
5
  import { displayMessageAfterSubmit, labelize } from '../../../utils/form';
8
6
 
9
7
  export default function AddMaterializationPopover({ node, onSubmit }) {
10
8
  const djClient = useContext(DJClientContext).DataJunctionAPI;
11
9
  const [popoverAnchor, setPopoverAnchor] = useState(false);
12
- const [engines, setEngines] = useState([]);
13
- const [defaultEngine, setDefaultEngine] = useState('');
10
+ const [options, setOptions] = useState([]);
11
+ const [jobs, setJobs] = useState([]);
14
12
 
15
13
  const ref = useRef(null);
16
14
 
17
15
  useEffect(() => {
18
16
  const fetchData = async () => {
19
- const engines = await djClient.engines();
20
- setEngines(engines);
21
- setDefaultEngine(
22
- engines && engines.length > 0
23
- ? engines[0].name + '__' + engines[0].version
24
- : '',
17
+ const options = await djClient.materializationInfo();
18
+ setOptions(options);
19
+ const allowedJobs = options.job_types?.filter(job =>
20
+ job.allowed_node_types.includes(node.type),
25
21
  );
22
+ setJobs(allowedJobs);
26
23
  };
27
24
  fetchData().catch(console.error);
28
25
  const handleClickOutside = event => {
@@ -41,14 +38,15 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
41
38
  { setSubmitting, setStatus },
42
39
  ) => {
43
40
  setSubmitting(false);
44
- const engineVersion = values.engine.split('__').slice(-1).join('');
45
- const engineName = values.engine.split('__').slice(0, -1).join('');
41
+ const config = JSON.parse(values.config);
42
+ config.lookback_window = values.lookback_window;
43
+ console.log('values', values);
46
44
  const response = await djClient.materialize(
47
45
  values.node,
48
- engineName,
49
- engineVersion,
46
+ values.job_type,
47
+ values.strategy,
50
48
  values.schedule,
51
- values.config,
49
+ config,
52
50
  );
53
51
  if (response.status === 200 || response.status === 201) {
54
52
  setStatus({ success: 'Saved!' });
@@ -65,7 +63,7 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
65
63
  <>
66
64
  <button
67
65
  className="edit_button"
68
- aria-label="PartitionColumn"
66
+ aria-label="AddMaterialization"
69
67
  tabIndex="0"
70
68
  onClick={() => {
71
69
  setPopoverAnchor(!popoverAnchor);
@@ -90,9 +88,11 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
90
88
  <Formik
91
89
  initialValues={{
92
90
  node: node?.name,
93
- engine: defaultEngine,
91
+ job_type: 'spark_sql',
92
+ strategy: 'full',
94
93
  config: '{"spark": {"spark.executor.memory": "6g"}}',
95
94
  schedule: '@daily',
95
+ lookback_window: '1 DAY',
96
96
  }}
97
97
  onSubmit={configureMaterialization}
98
98
  >
@@ -101,16 +101,15 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
101
101
  <Form>
102
102
  <h2>Configure Materialization</h2>
103
103
  {displayMessageAfterSubmit(status)}
104
- <span data-testid="edit-partition">
105
- <label htmlFor="engine">Engine</label>
106
- <Field as="select" name="engine">
104
+ <span data-testid="job-type">
105
+ <label htmlFor="job_type">Job Type</label>
106
+ <Field as="select" name="job_type">
107
107
  <>
108
- {engines?.map(engine => (
109
- <option value={engine.name + '__' + engine.version}>
110
- {engine.name} {engine.version}
108
+ {jobs?.map(job => (
109
+ <option key={job.name} value={job.name}>
110
+ {job.label}
111
111
  </option>
112
112
  ))}
113
- <option value=""></option>
114
113
  </>
115
114
  </Field>
116
115
  </span>
@@ -122,6 +121,18 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
122
121
  />
123
122
  <br />
124
123
  <br />
124
+ <span data-testid="edit-partition">
125
+ <label htmlFor="strategy">Strategy</label>
126
+ <Field as="select" name="strategy">
127
+ <>
128
+ {options.strategies?.map(strategy => (
129
+ <option value={strategy.name}>{strategy.label}</option>
130
+ ))}
131
+ </>
132
+ </Field>
133
+ </span>
134
+ <br />
135
+ <br />
125
136
  <label htmlFor="schedule">Schedule</label>
126
137
  <Field
127
138
  type="text"
@@ -132,6 +143,18 @@ export default function AddMaterializationPopover({ node, onSubmit }) {
132
143
  />
133
144
  <br />
134
145
  <br />
146
+ <div className="DescriptionInput">
147
+ <ErrorMessage name="description" component="span" />
148
+ <label htmlFor="Config">Lookback Window</label>
149
+ <Field
150
+ type="text"
151
+ name="lookback_window"
152
+ id="lookback_window"
153
+ placeholder="1 DAY"
154
+ default="1 DAY"
155
+ />
156
+ </div>
157
+ <br />
135
158
  <div className="DescriptionInput">
136
159
  <ErrorMessage name="description" component="span" />
137
160
  <label htmlFor="Config">Config</label>
@@ -53,11 +53,9 @@ export default function NodeMaterializationTab({ node, djClient }) {
53
53
  <div className={`cron-description`}>{cron(materialization)} </div>
54
54
  </td>
55
55
  <td>
56
- {materialization.engine.name}
57
- <br />
58
- {materialization.engine.version}
59
- <ClientCodePopover code={materialization.clientCode} />
56
+ {materialization.job?.replace('MaterializationJob', '').toUpperCase()}
60
57
  </td>
58
+ <td>{materialization.strategy?.toUpperCase()}</td>
61
59
  <td>
62
60
  {node.columns
63
61
  .filter(col => col.partition !== null)
@@ -90,27 +88,34 @@ export default function NodeMaterializationTab({ node, djClient }) {
90
88
  </div>
91
89
  ))}
92
90
  </td>
93
- <td>
94
- {materialization.backfills.map(backfill => (
95
- <a href={backfill.urls[0]} className="partitionLink">
96
- <div className="partition__full" key={backfill.spec.column_name}>
97
- <div className="partition__header">
98
- {partitionColumnsMap[backfill.spec.column_name]}
99
- </div>
100
- <div className="partition__body">
101
- <span className="badge partition_value">
102
- {backfill.spec.range[0]}
103
- </span>
104
- to
105
- <span className="badge partition_value">
106
- {backfill.spec.range[1]}
107
- </span>
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]}
101
+ </div>
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>
108
111
  </div>
109
- </div>
110
- </a>
111
- ))}
112
- <AddBackfillPopover node={node} materialization={materialization} />
113
- </td>
112
+ </a>
113
+ ))}
114
+ <AddBackfillPopover node={node} materialization={materialization} />
115
+ </td>
116
+ ) : (
117
+ <></>
118
+ )}
114
119
  <td>
115
120
  {materialization.urls.map((url, idx) => (
116
121
  <a href={url} key={`url-${idx}`}>
@@ -118,6 +123,9 @@ export default function NodeMaterializationTab({ node, djClient }) {
118
123
  </a>
119
124
  ))}
120
125
  </td>
126
+ <td>
127
+ <ClientCodePopover code={materialization.clientCode} />
128
+ </td>
121
129
  </tr>
122
130
  ));
123
131
  };
@@ -136,10 +144,15 @@ export default function NodeMaterializationTab({ node, djClient }) {
136
144
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
137
145
  <tr>
138
146
  <th className="text-start">Schedule</th>
139
- <th>Engine</th>
147
+ <th>Job Type</th>
148
+ <th>Strategy</th>
140
149
  <th>Partitions</th>
141
- <th>Output Tables</th>
142
- <th>Backfills</th>
150
+ <th>Intended Output Tables</th>
151
+ {materializations[0].strategy === 'incremental_time' ? (
152
+ <th>Backfills</th>
153
+ ) : (
154
+ <></>
155
+ )}
143
156
  <th>URLs</th>
144
157
  </tr>
145
158
  </thead>
@@ -171,16 +184,13 @@ export default function NodeMaterializationTab({ node, djClient }) {
171
184
  >
172
185
  <thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
173
186
  <tr>
174
- <th className="text-start">Catalog</th>
175
- <th>Schema</th>
176
- <th>Table</th>
187
+ <th className="text-start">Output Dataset</th>
177
188
  <th>Valid Through</th>
178
189
  <th>Partitions</th>
179
190
  </tr>
180
191
  </thead>
181
192
  <tbody>
182
193
  <tr>
183
- <td>{node.availability.schema_}</td>
184
194
  <td>
185
195
  {
186
196
  <div
@@ -203,7 +213,9 @@ export default function NodeMaterializationTab({ node, djClient }) {
203
213
  </div>
204
214
  }
205
215
  </td>
206
- <td>{node.availability.valid_through_ts}</td>
216
+ <td>
217
+ {new Date(node.availability.valid_through_ts).toISOString()}
218
+ </td>
207
219
  <td>
208
220
  <span
209
221
  className={`badge partition_value`}
@@ -0,0 +1,84 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
3
+ import DJClientContext from '../../../providers/djclient';
4
+ import AddMaterializationPopover from '../AddMaterializationPopover';
5
+ import { mocks } from '../../../../mocks/mockNodes';
6
+
7
+ const mockDjClient = {
8
+ DataJunctionAPI: {
9
+ materialize: jest.fn(),
10
+ materializationInfo: jest.fn(),
11
+ },
12
+ };
13
+
14
+ describe('<AddMaterializationPopover />', () => {
15
+ it('renders correctly and handles form submission', async () => {
16
+ // Mock onSubmit function
17
+ const onSubmitMock = jest.fn();
18
+ mockDjClient.DataJunctionAPI.materialize.mockReturnValue({
19
+ status: 201,
20
+ });
21
+ mockDjClient.DataJunctionAPI.materializationInfo.mockReturnValue({
22
+ status: 200,
23
+ json: {
24
+ job_types: [
25
+ {
26
+ name: 'spark_sql',
27
+ label: 'Spark SQL',
28
+ description: 'Spark SQL materialization job',
29
+ allowed_node_types: ['transform', 'dimension', 'cube'],
30
+ job_class: 'SparkSqlMaterializationJob',
31
+ },
32
+ {
33
+ name: 'druid_cube',
34
+ label: 'Druid Cube',
35
+ description:
36
+ 'Used to materialize a cube to Druid for low-latency access to a set of metrics and dimensions. While the logical cube definition is at the level of metrics and dimensions, a materialized Druid cube will reference measures and dimensions, with rollup configured on the measures where appropriate.',
37
+ allowed_node_types: ['cube'],
38
+ job_class: 'DruidCubeMaterializationJob',
39
+ },
40
+ ],
41
+ strategies: [
42
+ {
43
+ name: 'full',
44
+ label: 'Full',
45
+ },
46
+ {
47
+ name: 'snapshot',
48
+ label: 'Snapshot',
49
+ },
50
+ {
51
+ name: 'incremental_time',
52
+ label: 'Incremental Time',
53
+ },
54
+ {
55
+ name: 'view',
56
+ label: 'View',
57
+ },
58
+ ],
59
+ },
60
+ });
61
+
62
+ // Render the component
63
+ const { getByText } = render(
64
+ <DJClientContext.Provider value={mockDjClient}>
65
+ <AddMaterializationPopover
66
+ node={mocks.mockMetricNode}
67
+ onSubmit={onSubmitMock}
68
+ />
69
+ </DJClientContext.Provider>,
70
+ );
71
+
72
+ // Open the popover
73
+ fireEvent.click(getByText('+ Add Materialization'));
74
+
75
+ // Save the materialization
76
+ fireEvent.click(getByText('Save'));
77
+
78
+ // Expect setAttributes to be called
79
+ await waitFor(() => {
80
+ expect(mockDjClient.DataJunctionAPI.materialize).toHaveBeenCalled();
81
+ expect(getByText('Saved!')).toBeInTheDocument();
82
+ });
83
+ });
84
+ });
@@ -107,16 +107,16 @@ exports[`<NodePage /> renders the NodeMaterialization tab with materializations
107
107
  Schedule
108
108
  </th>
109
109
  <th>
110
- Engine
110
+ Job Type
111
111
  </th>
112
112
  <th>
113
- Partitions
113
+ Strategy
114
114
  </th>
115
115
  <th>
116
- Output Tables
116
+ Partitions
117
117
  </th>
118
118
  <th>
119
- Backfills
119
+ Intended Output Tables
120
120
  </th>
121
121
  <th>
122
122
  URLs
@@ -141,9 +141,80 @@ exports[`<NodePage /> renders the NodeMaterialization tab with materializations
141
141
  </div>
142
142
  </td>
143
143
  <td>
144
- spark
145
- <br />
146
- 2.4.4
144
+ SPARKSQL
145
+ </td>
146
+ <td />
147
+ <td />
148
+ <td>
149
+ <div
150
+ class="table__full"
151
+ >
152
+ <div
153
+ class="table__header"
154
+ >
155
+ <svg
156
+ class="bi bi-table"
157
+ fill="currentColor"
158
+ height="16"
159
+ viewBox="0 0 16 16"
160
+ width="16"
161
+ xmlns="http://www.w3.org/2000/svg"
162
+ >
163
+ <path
164
+ d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z"
165
+ />
166
+ </svg>
167
+
168
+ <span
169
+ class="entity-info"
170
+ >
171
+ common.a
172
+ </span>
173
+ </div>
174
+ <div
175
+ class="table__body upstream_tables"
176
+ />
177
+ </div>
178
+ <div
179
+ class="table__full"
180
+ >
181
+ <div
182
+ class="table__header"
183
+ >
184
+ <svg
185
+ class="bi bi-table"
186
+ fill="currentColor"
187
+ height="16"
188
+ viewBox="0 0 16 16"
189
+ width="16"
190
+ xmlns="http://www.w3.org/2000/svg"
191
+ >
192
+ <path
193
+ d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z"
194
+ />
195
+ </svg>
196
+
197
+ <span
198
+ class="entity-info"
199
+ >
200
+ common.b
201
+ </span>
202
+ </div>
203
+ <div
204
+ class="table__body upstream_tables"
205
+ />
206
+ </div>
207
+ </td>
208
+ <td>
209
+ <a
210
+ href="http://fake.url/job"
211
+ >
212
+ [
213
+ 1
214
+ ]
215
+ </a>
216
+ </td>
217
+ <td>
147
218
  <button
148
219
  aria-label="code-button"
149
220
  class="code-button"
@@ -230,173 +301,6 @@ exports[`<NodePage /> renders the NodeMaterialization tab with materializations
230
301
  </pre>
231
302
  </div>
232
303
  </td>
233
- <td />
234
- <td>
235
- <div
236
- class="table__full"
237
- >
238
- <div
239
- class="table__header"
240
- >
241
- <svg
242
- class="bi bi-table"
243
- fill="currentColor"
244
- height="16"
245
- viewBox="0 0 16 16"
246
- width="16"
247
- xmlns="http://www.w3.org/2000/svg"
248
- >
249
- <path
250
- d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z"
251
- />
252
- </svg>
253
-
254
- <span
255
- class="entity-info"
256
- >
257
- common.a
258
- </span>
259
- </div>
260
- <div
261
- class="table__body upstream_tables"
262
- />
263
- </div>
264
- <div
265
- class="table__full"
266
- >
267
- <div
268
- class="table__header"
269
- >
270
- <svg
271
- class="bi bi-table"
272
- fill="currentColor"
273
- height="16"
274
- viewBox="0 0 16 16"
275
- width="16"
276
- xmlns="http://www.w3.org/2000/svg"
277
- >
278
- <path
279
- d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm15 2h-4v3h4V4zm0 4h-4v3h4V8zm0 4h-4v3h3a1 1 0 0 0 1-1v-2zm-5 3v-3H6v3h4zm-5 0v-3H1v2a1 1 0 0 0 1 1h3zm-4-4h4V8H1v3zm0-4h4V4H1v3zm5-3v3h4V4H6zm4 4H6v3h4V8z"
280
- />
281
- </svg>
282
-
283
- <span
284
- class="entity-info"
285
- >
286
- common.b
287
- </span>
288
- </div>
289
- <div
290
- class="table__body upstream_tables"
291
- />
292
- </div>
293
- </td>
294
- <td>
295
- <a
296
- class="partitionLink"
297
- >
298
- <div
299
- class="partition__full"
300
- >
301
- <div
302
- class="partition__header"
303
- />
304
- <div
305
- class="partition__body"
306
- >
307
- <span
308
- class="badge partition_value"
309
- >
310
- 20230101
311
- </span>
312
- to
313
- <span
314
- class="badge partition_value"
315
- >
316
- 20230102
317
- </span>
318
- </div>
319
- </div>
320
- </a>
321
- <button
322
- aria-label="AddBackfill"
323
- class="edit_button"
324
- tabindex="0"
325
- >
326
- <span
327
- class="add_node"
328
- >
329
- + Add Backfill
330
- </span>
331
- </button>
332
- <div
333
- class="fade modal-backdrop in"
334
- style="display: none;"
335
- />
336
- <div
337
- aria-label="client-code"
338
- class="centerPopover"
339
- role="dialog"
340
- style="display: none; width: 50%;"
341
- >
342
- <form
343
- action="#"
344
- >
345
- <h2>
346
- Run Backfill
347
- </h2>
348
- <span
349
- data-testid="edit-partition"
350
- >
351
- <label
352
- for="engine"
353
- style="padding-bottom: 1rem;"
354
- >
355
- Engine
356
- </label>
357
- <select
358
- disabled=""
359
- id="engine"
360
- name="engine"
361
- >
362
- <option
363
- value="spark"
364
- >
365
- spark
366
-
367
- 2.4.4
368
- </option>
369
- </select>
370
- </span>
371
- <br />
372
- <br />
373
- <label
374
- for="partition"
375
- style="padding-bottom: 1rem;"
376
- >
377
- Partition Range
378
- </label>
379
- <br />
380
- <button
381
- aria-hidden="false"
382
- aria-label="SaveEditColumn"
383
- class="add_node"
384
- type="submit"
385
- >
386
- Save
387
- </button>
388
- </form>
389
- </div>
390
- </td>
391
- <td>
392
- <a
393
- href="http://fake.url/job"
394
- >
395
- [
396
- 1
397
- ]
398
- </a>
399
- </td>
400
304
  </tr>
401
305
  </tbody>
402
306
  </table>
@@ -696,13 +696,7 @@ export const DataJunctionAPI = {
696
696
  );
697
697
  return { status: response.status, json: await response.json() };
698
698
  },
699
- materialize: async function (
700
- nodeName,
701
- engineName,
702
- engineVersion,
703
- schedule,
704
- config,
705
- ) {
699
+ materialize: async function (nodeName, jobType, strategy, schedule, config) {
706
700
  const response = await fetch(
707
701
  `${DJ_URL}/nodes/${nodeName}/materialization`,
708
702
  {
@@ -711,12 +705,10 @@ export const DataJunctionAPI = {
711
705
  'Content-Type': 'application/json',
712
706
  },
713
707
  body: JSON.stringify({
714
- engine: {
715
- name: engineName,
716
- version: engineVersion,
717
- },
708
+ job: jobType,
709
+ strategy: strategy,
718
710
  schedule: schedule,
719
- config: JSON.parse(config),
711
+ config: config,
720
712
  }),
721
713
  credentials: 'include',
722
714
  },
@@ -756,4 +748,11 @@ export const DataJunctionAPI = {
756
748
  });
757
749
  return await response.json();
758
750
  },
751
+ materializationInfo: async function () {
752
+ return await (
753
+ await fetch(`${DJ_URL}/materialization/info`, {
754
+ credentials: 'include',
755
+ })
756
+ ).json();
757
+ },
759
758
  };
@@ -826,10 +826,10 @@ describe('DataJunctionAPI', () => {
826
826
  fetch.mockResponseOnce(JSON.stringify({}));
827
827
  await DataJunctionAPI.materialize(
828
828
  'default.hard_hat',
829
- 'spark',
830
- '3.3',
829
+ 'spark_sql',
830
+ 'full',
831
831
  '@daily',
832
- '{}',
832
+ {},
833
833
  );
834
834
  expect(fetch).toHaveBeenCalledWith(
835
835
  `${DJ_URL}/nodes/default.hard_hat/materialization`,
@@ -839,10 +839,8 @@ describe('DataJunctionAPI', () => {
839
839
  'Content-Type': 'application/json',
840
840
  },
841
841
  body: JSON.stringify({
842
- engine: {
843
- name: 'spark',
844
- version: '3.3',
845
- },
842
+ job: 'spark_sql',
843
+ strategy: 'full',
846
844
  schedule: '@daily',
847
845
  config: {},
848
846
  }),