datajunction-ui 0.0.1-a70.dev1 → 0.0.1-a72
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 +1 -1
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +3 -1
- package/src/app/pages/AddEditNodePage/index.jsx +9 -1
- package/src/app/pages/NamespacePage/Explorer.jsx +4 -1
- package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +10 -7
- package/src/app/pages/NamespacePage/TagSelect.jsx +5 -1
- package/src/app/pages/NamespacePage/UserSelect.jsx +5 -1
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +86 -80
- package/src/app/pages/NamespacePage/index.jsx +106 -50
- package/src/app/pages/NodePage/NodeMaterializationTab.jsx +16 -0
- package/src/app/pages/NodePage/NodeStatus.jsx +3 -1
- package/src/app/pages/NodePage/NotebookDownload.jsx +1 -1
- package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +148 -0
- package/src/app/pages/Root/index.tsx +1 -1
- package/src/app/services/DJService.js +1 -1
- package/src/app/services/__tests__/DJService.test.jsx +17 -6
- package/src/styles/index.css +62 -56
- package/src/styles/node-list.css +1 -1
package/package.json
CHANGED
|
@@ -138,7 +138,9 @@ describe('AddEditNodePage', () => {
|
|
|
138
138
|
).toMatchSnapshot();
|
|
139
139
|
|
|
140
140
|
// The namespace should be set to the one provided in params
|
|
141
|
-
|
|
141
|
+
screen
|
|
142
|
+
.getAllByText('default')
|
|
143
|
+
.forEach(element => expect(element).toBeInTheDocument());
|
|
142
144
|
});
|
|
143
145
|
});
|
|
144
146
|
|
|
@@ -313,7 +313,15 @@ export function AddEditNodePage() {
|
|
|
313
313
|
|
|
314
314
|
return (
|
|
315
315
|
<div className="mid">
|
|
316
|
-
<NamespaceHeader
|
|
316
|
+
<NamespaceHeader
|
|
317
|
+
namespace={
|
|
318
|
+
initialNamespace
|
|
319
|
+
? initialNamespace
|
|
320
|
+
: name
|
|
321
|
+
? name.substring(0, name.lastIndexOf('.'))
|
|
322
|
+
: ''
|
|
323
|
+
}
|
|
324
|
+
/>
|
|
317
325
|
<div className="card">
|
|
318
326
|
<div className="card-header">
|
|
319
327
|
{pageTitle}
|
|
@@ -45,7 +45,10 @@ const Explorer = ({ item = [], current }) => {
|
|
|
45
45
|
}}
|
|
46
46
|
key={index}
|
|
47
47
|
>
|
|
48
|
-
<div
|
|
48
|
+
<div
|
|
49
|
+
className={`${expand ? '' : 'inactive'}`}
|
|
50
|
+
key={`nested-${index}`}
|
|
51
|
+
>
|
|
49
52
|
<Explorer item={item} current={highlight} />
|
|
50
53
|
</div>
|
|
51
54
|
</div>
|
|
@@ -3,8 +3,11 @@ import Control from './FieldControl';
|
|
|
3
3
|
|
|
4
4
|
export default function NodeTypeSelect({ onChange }) {
|
|
5
5
|
return (
|
|
6
|
-
<span
|
|
7
|
-
|
|
6
|
+
<span
|
|
7
|
+
className="menu-link"
|
|
8
|
+
style={{ marginLeft: '30px', width: '300px' }}
|
|
9
|
+
data-testid="select-node-type"
|
|
10
|
+
>
|
|
8
11
|
<Select
|
|
9
12
|
name="node_type"
|
|
10
13
|
isClearable
|
|
@@ -15,11 +18,11 @@ export default function NodeTypeSelect({ onChange }) {
|
|
|
15
18
|
control: styles => ({ ...styles, backgroundColor: 'white' }),
|
|
16
19
|
}}
|
|
17
20
|
options={[
|
|
18
|
-
{value: 'source', label: 'Source'},
|
|
19
|
-
{value: 'transform', label: 'Transform'},
|
|
20
|
-
{value: 'dimension', label: 'Dimension'},
|
|
21
|
-
{value: 'metric', label: 'Metric'},
|
|
22
|
-
{value: 'cube', label: 'Cube'},
|
|
21
|
+
{ value: 'source', label: 'Source' },
|
|
22
|
+
{ value: 'transform', label: 'Transform' },
|
|
23
|
+
{ value: 'dimension', label: 'Dimension' },
|
|
24
|
+
{ value: 'metric', label: 'Metric' },
|
|
25
|
+
{ value: 'cube', label: 'Cube' },
|
|
23
26
|
]}
|
|
24
27
|
/>
|
|
25
28
|
</span>
|
|
@@ -20,7 +20,11 @@ export default function TagSelect({ onChange }) {
|
|
|
20
20
|
}, [djClient]);
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<span
|
|
23
|
+
<span
|
|
24
|
+
className="menu-link"
|
|
25
|
+
style={{ marginLeft: '30px', width: '350px' }}
|
|
26
|
+
data-testid="select-tag"
|
|
27
|
+
>
|
|
24
28
|
<Select
|
|
25
29
|
name="tags"
|
|
26
30
|
isClearable
|
|
@@ -19,7 +19,11 @@ export default function UserSelect({ onChange, currentUser }) {
|
|
|
19
19
|
}, [djClient]);
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
|
-
<span
|
|
22
|
+
<span
|
|
23
|
+
className="menu-link"
|
|
24
|
+
style={{ marginLeft: '30px', width: '400px' }}
|
|
25
|
+
data-testid="select-user"
|
|
26
|
+
>
|
|
23
27
|
{retrieved ? (
|
|
24
28
|
<Select
|
|
25
29
|
name="edited_by"
|
|
@@ -38,9 +38,15 @@ describe('NamespacePage', () => {
|
|
|
38
38
|
|
|
39
39
|
beforeEach(() => {
|
|
40
40
|
fetch.resetMocks();
|
|
41
|
-
mockDjClient.whoami.mockResolvedValue({username: 'dj'});
|
|
42
|
-
mockDjClient.users.mockResolvedValue([
|
|
43
|
-
|
|
41
|
+
mockDjClient.whoami.mockResolvedValue({ username: 'dj' });
|
|
42
|
+
mockDjClient.users.mockResolvedValue([
|
|
43
|
+
{ username: 'dj' },
|
|
44
|
+
{ username: 'user1' },
|
|
45
|
+
]);
|
|
46
|
+
mockDjClient.listTags.mockResolvedValue([
|
|
47
|
+
{ name: 'tag1' },
|
|
48
|
+
{ name: 'tag2' },
|
|
49
|
+
]);
|
|
44
50
|
mockDjClient.namespaces.mockResolvedValue([
|
|
45
51
|
{
|
|
46
52
|
namespace: 'common.one',
|
|
@@ -82,45 +88,43 @@ describe('NamespacePage', () => {
|
|
|
82
88
|
type: 'transform',
|
|
83
89
|
mode: 'active',
|
|
84
90
|
updated_at: new Date(),
|
|
85
|
-
tags: [{name: 'tag1'}],
|
|
91
|
+
tags: [{ name: 'tag1' }],
|
|
86
92
|
edited_by: ['dj'],
|
|
87
93
|
},
|
|
88
94
|
]);
|
|
89
|
-
mockDjClient.listNodesForLanding.mockResolvedValue(
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
mockDjClient.listNodesForLanding.mockResolvedValue({
|
|
96
|
+
data: {
|
|
97
|
+
findNodesPaginated: {
|
|
98
|
+
pageInfo: {
|
|
99
|
+
hasNextPage: true,
|
|
100
|
+
endCursor:
|
|
101
|
+
'eyJjcmVhdGVkX2F0IjogIjIwMjQtMDQtMTZUMjM6MjI6MjIuNDQxNjg2KzAwOjAwIiwgImlkIjogNjE0fQ==',
|
|
102
|
+
hasPrevPage: true,
|
|
103
|
+
startCursor:
|
|
104
|
+
'eyJjcmVhdGVkX2F0IjogIjIwMjQtMTAtMTZUMTY6MDM6MTcuMDgzMjY3KzAwOjAwIiwgImlkIjogMjQwOX0=',
|
|
105
|
+
},
|
|
106
|
+
edges: [
|
|
107
|
+
{
|
|
108
|
+
node: {
|
|
109
|
+
name: 'default.test_node',
|
|
110
|
+
type: 'DIMENSION',
|
|
111
|
+
currentVersion: 'v4.0',
|
|
112
|
+
tags: [],
|
|
113
|
+
editedBy: ['dj'],
|
|
114
|
+
current: {
|
|
115
|
+
displayName: 'Test Node',
|
|
116
|
+
status: 'VALID',
|
|
117
|
+
updatedAt: '2024-10-18T15:15:33.532949+00:00',
|
|
118
|
+
},
|
|
119
|
+
createdBy: {
|
|
120
|
+
username: 'dj',
|
|
121
|
+
},
|
|
98
122
|
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"currentVersion": "v4.0",
|
|
105
|
-
"tags": [],
|
|
106
|
-
"editedBy": [
|
|
107
|
-
"dj",
|
|
108
|
-
],
|
|
109
|
-
"current": {
|
|
110
|
-
"displayName": "Test Node",
|
|
111
|
-
"status": "VALID",
|
|
112
|
-
"updatedAt": "2024-10-18T15:15:33.532949+00:00"
|
|
113
|
-
},
|
|
114
|
-
"createdBy": {
|
|
115
|
-
"username": "dj"
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
);
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
124
128
|
});
|
|
125
129
|
|
|
126
130
|
afterEach(() => {
|
|
@@ -142,56 +146,58 @@ describe('NamespacePage', () => {
|
|
|
142
146
|
</MemoryRouter>,
|
|
143
147
|
);
|
|
144
148
|
|
|
145
|
-
await waitFor(
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
await waitFor(
|
|
150
|
+
() => {
|
|
151
|
+
expect(mockDjClient.listNodesForLanding).toHaveBeenCalled();
|
|
152
|
+
expect(screen.getByText('Namespaces')).toBeInTheDocument();
|
|
148
153
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
// check that it displays namespaces
|
|
155
|
+
expect(screen.getByText('common')).toBeInTheDocument();
|
|
156
|
+
expect(screen.getByText('one')).toBeInTheDocument();
|
|
157
|
+
expect(screen.getByText('fruits')).toBeInTheDocument();
|
|
158
|
+
expect(screen.getByText('vegetables')).toBeInTheDocument();
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
// check that it renders nodes
|
|
161
|
+
expect(screen.getByText('Test Node')).toBeInTheDocument();
|
|
157
162
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
163
|
+
// check that it sorts nodes
|
|
164
|
+
fireEvent.click(screen.getByText('name'));
|
|
165
|
+
fireEvent.click(screen.getByText('name'));
|
|
166
|
+
fireEvent.click(screen.getByText('display Name'));
|
|
162
167
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
// paginate
|
|
169
|
+
const previousButton = screen.getByText('← Previous');
|
|
170
|
+
expect(previousButton).toBeDefined();
|
|
171
|
+
fireEvent.click(previousButton);
|
|
172
|
+
const nextButton = screen.getByText('Next →');
|
|
173
|
+
expect(nextButton).toBeDefined();
|
|
174
|
+
fireEvent.click(nextButton);
|
|
170
175
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
// check that we can filter by node type
|
|
177
|
+
const selectNodeType = screen.getAllByTestId('select-node-type')[0];
|
|
178
|
+
expect(selectNodeType).toBeDefined();
|
|
179
|
+
expect(selectNodeType).not.toBeNull();
|
|
180
|
+
fireEvent.keyDown(selectNodeType.firstChild, { key: 'ArrowDown' });
|
|
181
|
+
fireEvent.click(screen.getByText('Source'));
|
|
177
182
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
+
// check that we can filter by tag
|
|
184
|
+
const selectTag = screen.getAllByTestId('select-tag')[0];
|
|
185
|
+
expect(selectTag).toBeDefined();
|
|
186
|
+
expect(selectTag).not.toBeNull();
|
|
187
|
+
fireEvent.keyDown(selectTag.firstChild, { key: 'ArrowDown' });
|
|
183
188
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
// check that we can filter by user
|
|
190
|
+
const selectUser = screen.getAllByTestId('select-user')[0];
|
|
191
|
+
expect(selectUser).toBeDefined();
|
|
192
|
+
expect(selectUser).not.toBeNull();
|
|
193
|
+
fireEvent.keyDown(selectUser.firstChild, { key: 'ArrowDown' });
|
|
189
194
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
// click to open and close tab
|
|
196
|
+
fireEvent.click(screen.getByText('common'));
|
|
197
|
+
fireEvent.click(screen.getByText('common'));
|
|
198
|
+
},
|
|
199
|
+
{ timeout: 3000 },
|
|
200
|
+
);
|
|
195
201
|
}, 60000);
|
|
196
202
|
|
|
197
203
|
it('can add new namespace via add namespace popover', async () => {
|
|
@@ -57,10 +57,16 @@ export function NamespacePage() {
|
|
|
57
57
|
let sortableData = [...Object.values(state.nodes)];
|
|
58
58
|
if (sortConfig !== null) {
|
|
59
59
|
sortableData.sort((a, b) => {
|
|
60
|
-
if (
|
|
60
|
+
if (
|
|
61
|
+
a[sortConfig.key] < b[sortConfig.key] ||
|
|
62
|
+
a.current[sortConfig.key] < b.current[sortConfig.key]
|
|
63
|
+
) {
|
|
61
64
|
return sortConfig.direction === ASC ? -1 : 1;
|
|
62
65
|
}
|
|
63
|
-
if (
|
|
66
|
+
if (
|
|
67
|
+
a[sortConfig.key] > b[sortConfig.key] ||
|
|
68
|
+
a.current[sortConfig.key] > b.current[sortConfig.key]
|
|
69
|
+
) {
|
|
64
70
|
return sortConfig.direction === ASC ? 1 : -1;
|
|
65
71
|
}
|
|
66
72
|
return 0;
|
|
@@ -131,19 +137,41 @@ export function NamespacePage() {
|
|
|
131
137
|
const nodes = await djClient.listNodesForLanding(
|
|
132
138
|
namespace,
|
|
133
139
|
filters.node_type ? [filters.node_type.toUpperCase()] : [],
|
|
134
|
-
filters.tags,
|
|
140
|
+
filters.tags,
|
|
141
|
+
filters.edited_by,
|
|
142
|
+
before,
|
|
143
|
+
after,
|
|
144
|
+
50,
|
|
145
|
+
);
|
|
135
146
|
console.log('nodes', nodes);
|
|
136
147
|
|
|
137
148
|
setState({
|
|
138
149
|
namespace: namespace,
|
|
139
|
-
nodes: nodes.data
|
|
150
|
+
nodes: nodes.data
|
|
151
|
+
? nodes.data.findNodesPaginated.edges.map(n => n.node)
|
|
152
|
+
: [],
|
|
140
153
|
});
|
|
141
154
|
if (nodes.data) {
|
|
142
|
-
setPrevCursor(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
setPrevCursor(
|
|
156
|
+
nodes.data ? nodes.data.findNodesPaginated.pageInfo.startCursor : '',
|
|
157
|
+
);
|
|
158
|
+
setNextCursor(
|
|
159
|
+
nodes.data ? nodes.data.findNodesPaginated.pageInfo.endCursor : '',
|
|
160
|
+
);
|
|
161
|
+
console.log(
|
|
162
|
+
'setting hasPrevPage, ',
|
|
163
|
+
nodes.data.findNodesPaginated.pageInfo.hasPrevPage,
|
|
164
|
+
);
|
|
165
|
+
setHasPrevPage(
|
|
166
|
+
nodes.data
|
|
167
|
+
? nodes.data.findNodesPaginated.pageInfo.hasPrevPage
|
|
168
|
+
: false,
|
|
169
|
+
);
|
|
170
|
+
setHasNextPage(
|
|
171
|
+
nodes.data
|
|
172
|
+
? nodes.data.findNodesPaginated.pageInfo.hasNextPage
|
|
173
|
+
: false,
|
|
174
|
+
);
|
|
147
175
|
}
|
|
148
176
|
setRetrieved(true);
|
|
149
177
|
};
|
|
@@ -164,51 +192,64 @@ export function NamespacePage() {
|
|
|
164
192
|
|
|
165
193
|
const nodesList = retrieved ? (
|
|
166
194
|
sortedNodes.length > 0 ? (
|
|
167
|
-
|
|
168
|
-
|
|
195
|
+
sortedNodes.map(node => (
|
|
196
|
+
<tr key={node.name}>
|
|
197
|
+
<td>
|
|
198
|
+
<a href={'/nodes/' + node.name} className="link-table">
|
|
199
|
+
{node.name}
|
|
200
|
+
</a>
|
|
201
|
+
<span
|
|
202
|
+
className="rounded-pill badge bg-secondary-soft"
|
|
203
|
+
style={{ marginLeft: '0.5rem' }}
|
|
204
|
+
>
|
|
205
|
+
{node.currentVersion}
|
|
206
|
+
</span>
|
|
207
|
+
</td>
|
|
208
|
+
<td>
|
|
209
|
+
<a href={'/nodes/' + node.name} className="link-table">
|
|
210
|
+
{node.type !== 'source' ? node.current.displayName : ''}
|
|
211
|
+
</a>
|
|
212
|
+
</td>
|
|
213
|
+
<td>
|
|
214
|
+
<span
|
|
215
|
+
className={
|
|
216
|
+
'node_type__' + node.type.toLowerCase() + ' badge node_type'
|
|
217
|
+
}
|
|
218
|
+
>
|
|
219
|
+
{node.type}
|
|
220
|
+
</span>
|
|
221
|
+
</td>
|
|
222
|
+
<td>
|
|
223
|
+
<NodeStatus node={node} revalidate={false} />
|
|
224
|
+
</td>
|
|
225
|
+
<td>
|
|
226
|
+
<span className="status">
|
|
227
|
+
{new Date(node.current.updatedAt).toLocaleString('en-us')}
|
|
228
|
+
</span>
|
|
229
|
+
</td>
|
|
230
|
+
<td>
|
|
231
|
+
<NodeListActions nodeName={node?.name} />
|
|
232
|
+
</td>
|
|
233
|
+
</tr>
|
|
234
|
+
))
|
|
235
|
+
) : (
|
|
236
|
+
<tr>
|
|
169
237
|
<td>
|
|
170
|
-
<a href={'/nodes/' + node.name} className="link-table">
|
|
171
|
-
{node.name}
|
|
172
|
-
</a>
|
|
173
238
|
<span
|
|
174
|
-
|
|
175
|
-
|
|
239
|
+
style={{
|
|
240
|
+
display: 'block',
|
|
241
|
+
marginTop: '2rem',
|
|
242
|
+
marginLeft: '2rem',
|
|
243
|
+
fontSize: '16px',
|
|
244
|
+
}}
|
|
176
245
|
>
|
|
177
|
-
{
|
|
246
|
+
There are no nodes in{' '}
|
|
247
|
+
<a href={`/namespaces/${namespace}`}>{namespace}</a> with the above
|
|
248
|
+
filters!
|
|
178
249
|
</span>
|
|
179
250
|
</td>
|
|
180
|
-
<td>
|
|
181
|
-
<a href={'/nodes/' + node.name} className="link-table">
|
|
182
|
-
{node.type !== 'source' ? node.current.displayName : ''}
|
|
183
|
-
</a>
|
|
184
|
-
</td>
|
|
185
|
-
<td>
|
|
186
|
-
<span className={'node_type__' + node.type.toLowerCase() + ' badge node_type'}>
|
|
187
|
-
{node.type}
|
|
188
|
-
</span>
|
|
189
|
-
</td>
|
|
190
|
-
<td>
|
|
191
|
-
<NodeStatus node={node} revalidate={false} />
|
|
192
|
-
</td>
|
|
193
|
-
<td>
|
|
194
|
-
<span className="status">
|
|
195
|
-
{new Date(node.current.updatedAt).toLocaleString('en-us')}
|
|
196
|
-
</span>
|
|
197
|
-
</td>
|
|
198
|
-
<td>
|
|
199
|
-
<NodeListActions nodeName={node?.name} />
|
|
200
|
-
</td>
|
|
201
251
|
</tr>
|
|
202
|
-
)
|
|
203
|
-
) : (
|
|
204
|
-
<tr>
|
|
205
|
-
<td>
|
|
206
|
-
<span style={{ display: 'block', marginTop: '2rem', marginLeft: '2rem', fontSize: '16px' }}>
|
|
207
|
-
There are no nodes in <a href={`/namespaces/${namespace}`}>{namespace}</a> with the above filters!
|
|
208
|
-
</span>
|
|
209
|
-
</td>
|
|
210
|
-
</tr>
|
|
211
|
-
)
|
|
252
|
+
)
|
|
212
253
|
) : (
|
|
213
254
|
<tr>
|
|
214
255
|
<td>
|
|
@@ -318,8 +359,23 @@ export function NamespacePage() {
|
|
|
318
359
|
<tfoot>
|
|
319
360
|
<tr>
|
|
320
361
|
<td>
|
|
321
|
-
{retrieved && hasPrevPage ?
|
|
322
|
-
|
|
362
|
+
{retrieved && hasPrevPage ? (
|
|
363
|
+
<a
|
|
364
|
+
onClick={loadPrev}
|
|
365
|
+
className="previous round pagination"
|
|
366
|
+
>
|
|
367
|
+
← Previous
|
|
368
|
+
</a>
|
|
369
|
+
) : (
|
|
370
|
+
''
|
|
371
|
+
)}
|
|
372
|
+
{retrieved && hasNextPage ? (
|
|
373
|
+
<a onClick={loadNext} className="next round pagination">
|
|
374
|
+
Next →
|
|
375
|
+
</a>
|
|
376
|
+
) : (
|
|
377
|
+
''
|
|
378
|
+
)}
|
|
323
379
|
</td>
|
|
324
380
|
</tr>
|
|
325
381
|
</tfoot>
|
|
@@ -256,6 +256,7 @@ export default function NodeMaterializationTab({ node, djClient }) {
|
|
|
256
256
|
<th className="text-start">Output Dataset</th>
|
|
257
257
|
<th>Valid Through</th>
|
|
258
258
|
<th>Partitions</th>
|
|
259
|
+
<th>Links</th>
|
|
259
260
|
</tr>
|
|
260
261
|
</thead>
|
|
261
262
|
<tbody>
|
|
@@ -299,6 +300,21 @@ export default function NodeMaterializationTab({ node, djClient }) {
|
|
|
299
300
|
</span>
|
|
300
301
|
</span>
|
|
301
302
|
</td>
|
|
303
|
+
<td>
|
|
304
|
+
{node.availability.links !== null ? (
|
|
305
|
+
Object.entries(node.availability.links).map(
|
|
306
|
+
([key, value]) => (
|
|
307
|
+
<div key={key}>
|
|
308
|
+
<a href={value} target="_blank" rel="noreferrer">
|
|
309
|
+
{key}
|
|
310
|
+
</a>
|
|
311
|
+
</div>
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
) : (
|
|
315
|
+
<></>
|
|
316
|
+
)}
|
|
317
|
+
</td>
|
|
302
318
|
</tr>
|
|
303
319
|
</tbody>
|
|
304
320
|
</table>
|
|
@@ -81,7 +81,9 @@ export default function NodeStatus({ node, revalidate = true }) {
|
|
|
81
81
|
<>
|
|
82
82
|
{revalidate && validation?.errors?.length > 0 ? (
|
|
83
83
|
displayValidation
|
|
84
|
-
) : validation?.status === 'valid' ||
|
|
84
|
+
) : validation?.status === 'valid' ||
|
|
85
|
+
node?.status === 'valid' ||
|
|
86
|
+
node?.current?.status === 'VALID' ? (
|
|
85
87
|
<span
|
|
86
88
|
className="status__valid status"
|
|
87
89
|
style={{ alignContent: 'center' }}
|
|
@@ -25,7 +25,7 @@ export default function NotebookDownload({ node }) {
|
|
|
25
25
|
<>
|
|
26
26
|
<div
|
|
27
27
|
className="badge download_notebook"
|
|
28
|
-
style={{cursor: 'pointer', backgroundColor: '#ffefd0'}}
|
|
28
|
+
style={{ cursor: 'pointer', backgroundColor: '#ffefd0' }}
|
|
29
29
|
tabIndex="0"
|
|
30
30
|
height="45px"
|
|
31
31
|
onClick={downloadFile}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, waitFor, screen } from '@testing-library/react';
|
|
3
|
+
import NodeMaterializationTab from '../NodeMaterializationTab';
|
|
4
|
+
|
|
5
|
+
describe('<NodeMaterializationTab />', () => {
|
|
6
|
+
const mockDjClient = {
|
|
7
|
+
node: jest.fn(),
|
|
8
|
+
materializations: jest.fn(),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const mockMaterializations = [
|
|
12
|
+
{
|
|
13
|
+
name: 'mat_one',
|
|
14
|
+
config: {},
|
|
15
|
+
schedule: '@daily',
|
|
16
|
+
job: 'SparkSqlMaterializationJob',
|
|
17
|
+
backfills: [
|
|
18
|
+
{
|
|
19
|
+
spec: [
|
|
20
|
+
{
|
|
21
|
+
column_name: 'date',
|
|
22
|
+
values: ['20200101'],
|
|
23
|
+
range: ['20201010'],
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
urls: ['https://example.com/'],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
strategy: 'full',
|
|
30
|
+
output_tables: ['table1'],
|
|
31
|
+
urls: ['https://example.com/'],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const mockNode = {
|
|
36
|
+
node_revision_id: 1,
|
|
37
|
+
node_id: 1,
|
|
38
|
+
type: 'source',
|
|
39
|
+
name: 'default.repair_orders',
|
|
40
|
+
display_name: 'Default: Repair Orders',
|
|
41
|
+
version: 'v1.0',
|
|
42
|
+
status: 'valid',
|
|
43
|
+
mode: 'published',
|
|
44
|
+
catalog: {
|
|
45
|
+
id: 1,
|
|
46
|
+
uuid: '0fc18295-e1a2-4c3c-b72a-894725c12488',
|
|
47
|
+
created_at: '2023-08-21T16:48:51.146121+00:00',
|
|
48
|
+
updated_at: '2023-08-21T16:48:51.146122+00:00',
|
|
49
|
+
extra_params: {},
|
|
50
|
+
name: 'warehouse',
|
|
51
|
+
},
|
|
52
|
+
schema_: 'roads',
|
|
53
|
+
table: 'repair_orders',
|
|
54
|
+
description: 'Repair orders',
|
|
55
|
+
query: null,
|
|
56
|
+
availability: {
|
|
57
|
+
catalog: 'default',
|
|
58
|
+
categorical_partitions: [],
|
|
59
|
+
max_temporal_partition: ['2023', '01', '25'],
|
|
60
|
+
min_temporal_partition: ['2022', '01', '01'],
|
|
61
|
+
partitions: [],
|
|
62
|
+
schema_: 'foo',
|
|
63
|
+
table: 'bar',
|
|
64
|
+
temporal_partitions: [],
|
|
65
|
+
valid_through_ts: 1729667463,
|
|
66
|
+
url: 'https://www.table.com',
|
|
67
|
+
links: { dashboard: 'https://www.foobar.com/dashboard' },
|
|
68
|
+
},
|
|
69
|
+
columns: [
|
|
70
|
+
{
|
|
71
|
+
name: 'repair_order_id',
|
|
72
|
+
type: 'int',
|
|
73
|
+
attributes: [],
|
|
74
|
+
dimension: null,
|
|
75
|
+
partition: {
|
|
76
|
+
type_: 'temporal',
|
|
77
|
+
format: 'YYYYMMDD',
|
|
78
|
+
granularity: 'day',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'municipality_id',
|
|
83
|
+
type: 'string',
|
|
84
|
+
attributes: [],
|
|
85
|
+
dimension: null,
|
|
86
|
+
partition: null,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'hard_hat_id',
|
|
90
|
+
type: 'int',
|
|
91
|
+
attributes: [],
|
|
92
|
+
dimension: null,
|
|
93
|
+
partition: null,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
updated_at: '2023-08-21T16:48:52.880498+00:00',
|
|
97
|
+
materializations: [
|
|
98
|
+
{
|
|
99
|
+
name: 'mat1',
|
|
100
|
+
config: {},
|
|
101
|
+
schedule: 'string',
|
|
102
|
+
job: 'string',
|
|
103
|
+
backfills: [
|
|
104
|
+
{
|
|
105
|
+
spec: [
|
|
106
|
+
{
|
|
107
|
+
column_name: 'string',
|
|
108
|
+
values: ['string'],
|
|
109
|
+
range: ['string'],
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
urls: ['string'],
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
strategy: 'string',
|
|
116
|
+
output_tables: ['string'],
|
|
117
|
+
urls: ['https://example.com/'],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
parents: [],
|
|
121
|
+
dimension_links: [
|
|
122
|
+
{
|
|
123
|
+
dimension: {
|
|
124
|
+
name: 'default.contractor',
|
|
125
|
+
},
|
|
126
|
+
join_type: 'left',
|
|
127
|
+
join_sql:
|
|
128
|
+
'default.contractor.contractor_id = default.repair_orders.contractor_id',
|
|
129
|
+
join_cardinality: 'one_to_one',
|
|
130
|
+
role: 'contractor',
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
beforeEach(() => {
|
|
136
|
+
mockDjClient.materializations.mockReset();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('renders NodeMaterializationTab tab correctly', async () => {
|
|
140
|
+
mockDjClient.materializations.mockReturnValue(mockMaterializations);
|
|
141
|
+
|
|
142
|
+
render(<NodeMaterializationTab node={mockNode} djClient={mockDjClient} />);
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
const link = screen.getByText('dashboard').closest('a');
|
|
145
|
+
expect(link).toHaveAttribute('href', `https://www.foobar.com/dashboard`);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -6,7 +6,7 @@ const DJ_URL = process.env.REACT_APP_DJ_URL
|
|
|
6
6
|
|
|
7
7
|
const DJ_GQL = process.env.REACT_APP_DJ_GQL
|
|
8
8
|
? process.env.REACT_APP_DJ_GQL
|
|
9
|
-
: '
|
|
9
|
+
: process.env.REACT_APP_DJ_URL + '/graphql';
|
|
10
10
|
|
|
11
11
|
export const DataJunctionAPI = {
|
|
12
12
|
listNodesForLanding: async function (
|
|
@@ -426,9 +426,12 @@ describe('DataJunctionAPI', () => {
|
|
|
426
426
|
const nmspce = 'sampleNamespace';
|
|
427
427
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
428
428
|
await DataJunctionAPI.namespace(nmspce);
|
|
429
|
-
expect(fetch).toHaveBeenCalledWith(
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
430
|
+
`${DJ_URL}/namespaces/${nmspce}?edited_by=undefined&with_edited_by=true`,
|
|
431
|
+
{
|
|
432
|
+
credentials: 'include',
|
|
433
|
+
},
|
|
434
|
+
);
|
|
432
435
|
});
|
|
433
436
|
|
|
434
437
|
it('calls sql correctly', async () => {
|
|
@@ -489,9 +492,9 @@ describe('DataJunctionAPI', () => {
|
|
|
489
492
|
const sampleNode = {
|
|
490
493
|
name: 'sampleNode',
|
|
491
494
|
columns: [
|
|
492
|
-
{ name: 'column1', dimension: { name: 'dimension1' }
|
|
495
|
+
{ name: 'column1', dimension: { name: 'dimension1' } },
|
|
493
496
|
{ name: 'column2', dimension: null },
|
|
494
|
-
{ name: 'column3', dimension: { name: 'dimension2' }
|
|
497
|
+
{ name: 'column3', dimension: { name: 'dimension2' } },
|
|
495
498
|
],
|
|
496
499
|
};
|
|
497
500
|
|
|
@@ -1073,7 +1076,15 @@ describe('DataJunctionAPI', () => {
|
|
|
1073
1076
|
it('calls listNodesForLanding correctly', () => {
|
|
1074
1077
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
1075
1078
|
|
|
1076
|
-
DataJunctionAPI.listNodesForLanding(
|
|
1079
|
+
DataJunctionAPI.listNodesForLanding(
|
|
1080
|
+
'',
|
|
1081
|
+
['source'],
|
|
1082
|
+
[],
|
|
1083
|
+
'',
|
|
1084
|
+
null,
|
|
1085
|
+
null,
|
|
1086
|
+
100,
|
|
1087
|
+
);
|
|
1077
1088
|
expect(fetch).toHaveBeenCalledWith(
|
|
1078
1089
|
`${DJ_URL}/graphql`,
|
|
1079
1090
|
expect.objectContaining({
|
package/src/styles/index.css
CHANGED
|
@@ -1284,67 +1284,67 @@ pre {
|
|
|
1284
1284
|
|
|
1285
1285
|
.backfills {
|
|
1286
1286
|
margin-left: -4rem;
|
|
1287
|
-
--spacing
|
|
1288
|
-
--radius
|
|
1287
|
+
--spacing: 1.5rem;
|
|
1288
|
+
--radius: 10px;
|
|
1289
1289
|
}
|
|
1290
1290
|
|
|
1291
|
-
.backfills li{
|
|
1292
|
-
display
|
|
1293
|
-
position
|
|
1294
|
-
padding-left
|
|
1291
|
+
.backfills li {
|
|
1292
|
+
display: block;
|
|
1293
|
+
position: relative;
|
|
1294
|
+
padding-left: calc(2 * var(--spacing) - var(--radius) - 2px);
|
|
1295
1295
|
}
|
|
1296
1296
|
|
|
1297
|
-
.backfills ul{
|
|
1298
|
-
margin-left
|
|
1299
|
-
padding-left
|
|
1297
|
+
.backfills ul {
|
|
1298
|
+
margin-left: calc(var(--radius) - var(--spacing));
|
|
1299
|
+
padding-left: 2rem;
|
|
1300
1300
|
}
|
|
1301
1301
|
|
|
1302
|
-
.backfills ul li{
|
|
1303
|
-
border-left
|
|
1302
|
+
.backfills ul li {
|
|
1303
|
+
border-left: 2px solid #ddd;
|
|
1304
1304
|
}
|
|
1305
1305
|
|
|
1306
|
-
.backfills ul li:last-child{
|
|
1307
|
-
border-color
|
|
1306
|
+
.backfills ul li:last-child {
|
|
1307
|
+
border-color: transparent;
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
1310
|
-
.backfills ul li::before{
|
|
1311
|
-
content
|
|
1312
|
-
display
|
|
1313
|
-
position
|
|
1314
|
-
top
|
|
1315
|
-
left
|
|
1316
|
-
width
|
|
1317
|
-
height
|
|
1318
|
-
border
|
|
1319
|
-
border-width
|
|
1310
|
+
.backfills ul li::before {
|
|
1311
|
+
content: '';
|
|
1312
|
+
display: block;
|
|
1313
|
+
position: absolute;
|
|
1314
|
+
top: calc(var(--spacing) / -2);
|
|
1315
|
+
left: -2px;
|
|
1316
|
+
width: calc(var(--spacing) + 2px);
|
|
1317
|
+
height: calc(var(--spacing) + 1px);
|
|
1318
|
+
border: solid #ddd;
|
|
1319
|
+
border-width: 0 0 2px 2px;
|
|
1320
1320
|
}
|
|
1321
1321
|
|
|
1322
|
-
.backfills summary{
|
|
1323
|
-
display
|
|
1324
|
-
cursor
|
|
1322
|
+
.backfills summary {
|
|
1323
|
+
display: block;
|
|
1324
|
+
cursor: pointer;
|
|
1325
1325
|
margin-bottom: 10px;
|
|
1326
1326
|
}
|
|
1327
1327
|
|
|
1328
1328
|
.backfills summary::marker,
|
|
1329
|
-
.backfills summary::-webkit-details-marker{
|
|
1330
|
-
display
|
|
1329
|
+
.backfills summary::-webkit-details-marker {
|
|
1330
|
+
display: none;
|
|
1331
1331
|
}
|
|
1332
1332
|
|
|
1333
|
-
.backfills summary:focus{
|
|
1334
|
-
outline
|
|
1333
|
+
.backfills summary:focus {
|
|
1334
|
+
outline: none;
|
|
1335
1335
|
}
|
|
1336
1336
|
|
|
1337
|
-
.backfills summary:focus-visible{
|
|
1338
|
-
outline
|
|
1337
|
+
.backfills summary:focus-visible {
|
|
1338
|
+
outline: 1px dotted #000;
|
|
1339
1339
|
}
|
|
1340
1340
|
|
|
1341
|
-
.backfills summary::before{
|
|
1342
|
-
z-index
|
|
1341
|
+
.backfills summary::before {
|
|
1342
|
+
z-index: 1;
|
|
1343
1343
|
/*background : #696 url('expand-collapse.svg') 0 0;*/
|
|
1344
1344
|
}
|
|
1345
1345
|
|
|
1346
|
-
.backfills details[open] > summary::before{
|
|
1347
|
-
background-position
|
|
1346
|
+
.backfills details[open] > summary::before {
|
|
1347
|
+
background-position: calc(-2 * var(--radius)) 0;
|
|
1348
1348
|
}
|
|
1349
1349
|
|
|
1350
1350
|
.backfills_header {
|
|
@@ -1371,32 +1371,32 @@ pre {
|
|
|
1371
1371
|
border-radius: 2px;
|
|
1372
1372
|
border-style: none;
|
|
1373
1373
|
padding: 0.4rem;
|
|
1374
|
-
font-family: Lato,
|
|
1374
|
+
font-family: Lato, 'sans-serif';
|
|
1375
1375
|
font-size: 110%;
|
|
1376
1376
|
border-right: 16px solid transparent;
|
|
1377
1377
|
margin-left: 0.4rem;
|
|
1378
1378
|
}
|
|
1379
1379
|
|
|
1380
1380
|
.backfills summary::marker,
|
|
1381
|
-
.backfills summary::-webkit-details-marker{
|
|
1382
|
-
display
|
|
1381
|
+
.backfills summary::-webkit-details-marker {
|
|
1382
|
+
display: none;
|
|
1383
1383
|
}
|
|
1384
1384
|
|
|
1385
|
-
.backfills summary:focus{
|
|
1386
|
-
outline
|
|
1385
|
+
.backfills summary:focus {
|
|
1386
|
+
outline: none;
|
|
1387
1387
|
}
|
|
1388
1388
|
|
|
1389
|
-
.backfills summary:focus-visible{
|
|
1390
|
-
outline
|
|
1389
|
+
.backfills summary:focus-visible {
|
|
1390
|
+
outline: 1px dotted #000;
|
|
1391
1391
|
}
|
|
1392
1392
|
|
|
1393
|
-
.backfills summary::before{
|
|
1394
|
-
z-index
|
|
1393
|
+
.backfills summary::before {
|
|
1394
|
+
z-index: 1;
|
|
1395
1395
|
/*background : #696 url('expand-collapse.svg') 0 0;*/
|
|
1396
1396
|
}
|
|
1397
1397
|
|
|
1398
|
-
.backfills details[open] > summary::before{
|
|
1399
|
-
background-position
|
|
1398
|
+
.backfills details[open] > summary::before {
|
|
1399
|
+
background-position: calc(-2 * var(--radius)) 0;
|
|
1400
1400
|
}
|
|
1401
1401
|
|
|
1402
1402
|
.backfills_header {
|
|
@@ -1445,8 +1445,8 @@ table {
|
|
|
1445
1445
|
position: absolute;
|
|
1446
1446
|
z-index: 1;
|
|
1447
1447
|
|
|
1448
|
-
|
|
1449
|
-
|
|
1448
|
+
top: -5px;
|
|
1449
|
+
left: 125%;
|
|
1450
1450
|
/*bottom: 125%;*/
|
|
1451
1451
|
/*left: 50%;*/
|
|
1452
1452
|
margin-left: -60px;
|
|
@@ -1455,7 +1455,7 @@ table {
|
|
|
1455
1455
|
}
|
|
1456
1456
|
|
|
1457
1457
|
.tooltip .tooltiptext::after {
|
|
1458
|
-
content:
|
|
1458
|
+
content: '';
|
|
1459
1459
|
position: absolute;
|
|
1460
1460
|
top: 100%;
|
|
1461
1461
|
left: 50%;
|
|
@@ -1476,13 +1476,19 @@ table {
|
|
|
1476
1476
|
grid-template-rows: 250px 1fr;
|
|
1477
1477
|
gap: 20px;
|
|
1478
1478
|
grid-template-areas:
|
|
1479
|
-
|
|
1480
|
-
|
|
1479
|
+
'left righttop'
|
|
1480
|
+
'left rightbottom';
|
|
1481
1481
|
margin-top: 1rem;
|
|
1482
1482
|
}
|
|
1483
|
-
.left {
|
|
1484
|
-
|
|
1485
|
-
|
|
1483
|
+
.left {
|
|
1484
|
+
grid-area: left;
|
|
1485
|
+
}
|
|
1486
|
+
.righttop {
|
|
1487
|
+
grid-area: righttop;
|
|
1488
|
+
}
|
|
1489
|
+
.rightbottom {
|
|
1490
|
+
grid-area: rightbottom;
|
|
1491
|
+
}
|
|
1486
1492
|
|
|
1487
1493
|
.queryrunner-query pre {
|
|
1488
1494
|
border-radius: 0;
|
|
@@ -1538,4 +1544,4 @@ table {
|
|
|
1538
1544
|
}
|
|
1539
1545
|
.dropdown:hover .dropdown-menu {
|
|
1540
1546
|
display: block;
|
|
1541
|
-
}
|
|
1547
|
+
}
|
package/src/styles/node-list.css
CHANGED