datajunction-ui 0.0.1-a70 → 0.0.1-a70.dev1
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/icons/AddItemIcon.jsx +1 -1
- package/src/app/pages/NamespacePage/Explorer.jsx +3 -2
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +50 -9
- package/src/app/pages/NamespacePage/index.jsx +79 -40
- package/src/app/pages/NodePage/NodeStatus.jsx +1 -1
- package/src/app/pages/Root/index.tsx +17 -6
- package/src/app/services/DJService.js +84 -4
- package/src/app/services/__tests__/DJService.test.jsx +17 -1
- package/src/styles/index.css +26 -0
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ const Explorer = ({ item = [], current }) => {
|
|
|
10
10
|
useEffect(() => {
|
|
11
11
|
setItems(item);
|
|
12
12
|
setHighlight(current);
|
|
13
|
-
if (current
|
|
13
|
+
if (current !== undefined && current?.startsWith(item.path)) {
|
|
14
14
|
setExpand(true);
|
|
15
15
|
} else setExpand(false);
|
|
16
16
|
}, [current, item]);
|
|
@@ -43,8 +43,9 @@ const Explorer = ({ item = [], current }) => {
|
|
|
43
43
|
marginLeft: '1rem',
|
|
44
44
|
borderLeft: '1px solid rgb(218 233 255)',
|
|
45
45
|
}}
|
|
46
|
+
key={index}
|
|
46
47
|
>
|
|
47
|
-
<div className={`${expand ? '' : 'inactive'}`}>
|
|
48
|
+
<div className={`${expand ? '' : 'inactive'}`} key={`nested-${index}`}>
|
|
48
49
|
<Explorer item={item} current={highlight} />
|
|
49
50
|
</div>
|
|
50
51
|
</div>
|
|
@@ -8,6 +8,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
8
8
|
const mockDjClient = {
|
|
9
9
|
namespaces: jest.fn(),
|
|
10
10
|
namespace: jest.fn(),
|
|
11
|
+
listNodesForLanding: jest.fn(),
|
|
11
12
|
addNamespace: jest.fn(),
|
|
12
13
|
whoami: jest.fn(),
|
|
13
14
|
users: jest.fn(),
|
|
@@ -85,6 +86,41 @@ describe('NamespacePage', () => {
|
|
|
85
86
|
edited_by: ['dj'],
|
|
86
87
|
},
|
|
87
88
|
]);
|
|
89
|
+
mockDjClient.listNodesForLanding.mockResolvedValue(
|
|
90
|
+
{
|
|
91
|
+
"data": {
|
|
92
|
+
"findNodesPaginated": {
|
|
93
|
+
"pageInfo": {
|
|
94
|
+
"hasNextPage": true,
|
|
95
|
+
"endCursor": "eyJjcmVhdGVkX2F0IjogIjIwMjQtMDQtMTZUMjM6MjI6MjIuNDQxNjg2KzAwOjAwIiwgImlkIjogNjE0fQ==",
|
|
96
|
+
"hasPrevPage": true,
|
|
97
|
+
"startCursor": "eyJjcmVhdGVkX2F0IjogIjIwMjQtMTAtMTZUMTY6MDM6MTcuMDgzMjY3KzAwOjAwIiwgImlkIjogMjQwOX0="
|
|
98
|
+
},
|
|
99
|
+
"edges": [
|
|
100
|
+
{
|
|
101
|
+
"node": {
|
|
102
|
+
"name": "default.test_node",
|
|
103
|
+
"type": "DIMENSION",
|
|
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
|
+
);
|
|
88
124
|
});
|
|
89
125
|
|
|
90
126
|
afterEach(() => {
|
|
@@ -107,7 +143,7 @@ describe('NamespacePage', () => {
|
|
|
107
143
|
);
|
|
108
144
|
|
|
109
145
|
await waitFor(() => {
|
|
110
|
-
expect(mockDjClient.
|
|
146
|
+
expect(mockDjClient.listNodesForLanding).toHaveBeenCalled();
|
|
111
147
|
expect(screen.getByText('Namespaces')).toBeInTheDocument();
|
|
112
148
|
|
|
113
149
|
// check that it displays namespaces
|
|
@@ -121,7 +157,16 @@ describe('NamespacePage', () => {
|
|
|
121
157
|
|
|
122
158
|
// check that it sorts nodes
|
|
123
159
|
fireEvent.click(screen.getByText('name'));
|
|
124
|
-
fireEvent.click(screen.getByText('
|
|
160
|
+
fireEvent.click(screen.getByText('name'));
|
|
161
|
+
fireEvent.click(screen.getByText('display Name'));
|
|
162
|
+
|
|
163
|
+
// paginate
|
|
164
|
+
const previousButton = screen.getByText('← Previous');
|
|
165
|
+
expect(previousButton).toBeDefined();
|
|
166
|
+
fireEvent.click(previousButton);
|
|
167
|
+
const nextButton = screen.getByText('Next →');
|
|
168
|
+
expect(nextButton).toBeDefined();
|
|
169
|
+
fireEvent.click(nextButton);
|
|
125
170
|
|
|
126
171
|
// check that we can filter by node type
|
|
127
172
|
const selectNodeType = screen.getAllByTestId('select-node-type')[0];
|
|
@@ -141,17 +186,13 @@ describe('NamespacePage', () => {
|
|
|
141
186
|
expect(selectUser).toBeDefined();
|
|
142
187
|
expect(selectUser).not.toBeNull();
|
|
143
188
|
fireEvent.keyDown(selectUser.firstChild, { key: 'ArrowDown' });
|
|
144
|
-
// fireEvent.click(screen.getByText('dj'));
|
|
145
189
|
|
|
146
190
|
// click to open and close tab
|
|
147
191
|
fireEvent.click(screen.getByText('common'));
|
|
148
192
|
fireEvent.click(screen.getByText('common'));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
afterEach(() => {
|
|
153
|
-
jest.clearAllMocks();
|
|
154
|
-
});
|
|
193
|
+
},
|
|
194
|
+
{ timeout: 3000 });
|
|
195
|
+
}, 60000);
|
|
155
196
|
|
|
156
197
|
it('can add new namespace via add namespace popover', async () => {
|
|
157
198
|
mockDjClient.addNamespace.mockReturnValue({
|
|
@@ -20,13 +20,13 @@ export function NamespacePage() {
|
|
|
20
20
|
const ASC = 'ascending';
|
|
21
21
|
const DESC = 'descending';
|
|
22
22
|
|
|
23
|
-
const fields = ['name', '
|
|
23
|
+
const fields = ['name', 'displayName', 'type', 'status', 'updatedAt'];
|
|
24
24
|
|
|
25
25
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
26
26
|
var { namespace } = useParams();
|
|
27
27
|
|
|
28
28
|
const [state, setState] = useState({
|
|
29
|
-
namespace: namespace,
|
|
29
|
+
namespace: namespace ? namespace : '',
|
|
30
30
|
nodes: [],
|
|
31
31
|
});
|
|
32
32
|
const [retrieved, setRetrieved] = useState(false);
|
|
@@ -35,39 +35,32 @@ export function NamespacePage() {
|
|
|
35
35
|
const [filters, setFilters] = useState({
|
|
36
36
|
tags: [],
|
|
37
37
|
node_type: '',
|
|
38
|
-
edited_by:
|
|
38
|
+
edited_by: '',
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
const [namespaceHierarchy, setNamespaceHierarchy] = useState([]);
|
|
42
42
|
|
|
43
43
|
const [sortConfig, setSortConfig] = useState({
|
|
44
|
-
key: '
|
|
44
|
+
key: 'updatedAt',
|
|
45
45
|
direction: DESC,
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
const [before, setBefore] = useState(null);
|
|
49
|
+
const [after, setAfter] = useState(null);
|
|
50
|
+
const [prevCursor, setPrevCursor] = useState(true);
|
|
51
|
+
const [nextCursor, setNextCursor] = useState(true);
|
|
52
|
+
|
|
53
|
+
const [hasNextPage, setHasNextPage] = useState(true);
|
|
54
|
+
const [hasPrevPage, setHasPrevPage] = useState(true);
|
|
55
|
+
|
|
47
56
|
const sortedNodes = React.useMemo(() => {
|
|
48
57
|
let sortableData = [...Object.values(state.nodes)];
|
|
49
|
-
if (filters.node_type !== '' && filters.node_type !== null) {
|
|
50
|
-
sortableData = sortableData.filter(
|
|
51
|
-
node => node.type === filters.node_type,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (filters.tags) {
|
|
55
|
-
sortableData = sortableData.filter(node => {
|
|
56
|
-
const nodeTags = node.tags.map(tag => tag.name);
|
|
57
|
-
return filters.tags.every(item => nodeTags.includes(item));
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
if (filters.edited_by) {
|
|
61
|
-
sortableData = sortableData.filter(node => {
|
|
62
|
-
return node.edited_by.includes(filters.edited_by);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
58
|
if (sortConfig !== null) {
|
|
66
59
|
sortableData.sort((a, b) => {
|
|
67
|
-
if (a[sortConfig.key] < b[sortConfig.key]) {
|
|
60
|
+
if (a[sortConfig.key] < b[sortConfig.key] || a.current[sortConfig.key] < b.current[sortConfig.key]) {
|
|
68
61
|
return sortConfig.direction === ASC ? -1 : 1;
|
|
69
62
|
}
|
|
70
|
-
if (a[sortConfig.key] > b[sortConfig.key]) {
|
|
63
|
+
if (a[sortConfig.key] > b[sortConfig.key] || a.current[sortConfig.key] > b.current[sortConfig.key]) {
|
|
71
64
|
return sortConfig.direction === ASC ? 1 : -1;
|
|
72
65
|
}
|
|
73
66
|
return 0;
|
|
@@ -125,6 +118,7 @@ export function NamespacePage() {
|
|
|
125
118
|
const hierarchy = createNamespaceHierarchy(namespaces);
|
|
126
119
|
setNamespaceHierarchy(hierarchy);
|
|
127
120
|
const currentUser = await djClient.whoami();
|
|
121
|
+
// setFilters({...filters, edited_by: currentUser?.username});
|
|
128
122
|
setCurrentUser(currentUser);
|
|
129
123
|
};
|
|
130
124
|
fetchData().catch(console.error);
|
|
@@ -132,23 +126,46 @@ export function NamespacePage() {
|
|
|
132
126
|
|
|
133
127
|
useEffect(() => {
|
|
134
128
|
const fetchData = async () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
setRetrieved(false);
|
|
130
|
+
console.log('cursor', before, filters.edited_by);
|
|
131
|
+
const nodes = await djClient.listNodesForLanding(
|
|
132
|
+
namespace,
|
|
133
|
+
filters.node_type ? [filters.node_type.toUpperCase()] : [],
|
|
134
|
+
filters.tags, filters.edited_by, before, after, 50);
|
|
135
|
+
console.log('nodes', nodes);
|
|
136
|
+
|
|
140
137
|
setState({
|
|
141
138
|
namespace: namespace,
|
|
142
|
-
nodes:
|
|
139
|
+
nodes: nodes.data ? nodes.data.findNodesPaginated.edges.map(n => n.node) : [],
|
|
143
140
|
});
|
|
141
|
+
if (nodes.data) {
|
|
142
|
+
setPrevCursor(nodes.data ? nodes.data.findNodesPaginated.pageInfo.startCursor : '');
|
|
143
|
+
setNextCursor(nodes.data ? nodes.data.findNodesPaginated.pageInfo.endCursor : '');
|
|
144
|
+
console.log('setting hasPrevPage, ', nodes.data.findNodesPaginated.pageInfo.hasPrevPage);
|
|
145
|
+
setHasPrevPage(nodes.data ? nodes.data.findNodesPaginated.pageInfo.hasPrevPage : false);
|
|
146
|
+
setHasNextPage(nodes.data ? nodes.data.findNodesPaginated.pageInfo.hasNextPage : false);
|
|
147
|
+
}
|
|
144
148
|
setRetrieved(true);
|
|
145
149
|
};
|
|
146
150
|
fetchData().catch(console.error);
|
|
147
|
-
}, [djClient,
|
|
151
|
+
}, [djClient, filters, before, after]);
|
|
152
|
+
const loadNext = () => {
|
|
153
|
+
if (nextCursor) {
|
|
154
|
+
setAfter(nextCursor);
|
|
155
|
+
setBefore(null);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const loadPrev = () => {
|
|
159
|
+
if (prevCursor) {
|
|
160
|
+
setAfter(null);
|
|
161
|
+
setBefore(prevCursor);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
148
164
|
|
|
149
165
|
const nodesList = retrieved ? (
|
|
166
|
+
sortedNodes.length > 0 ? (
|
|
150
167
|
sortedNodes.map(node => (
|
|
151
|
-
<tr>
|
|
168
|
+
<tr key={node.name}>
|
|
152
169
|
<td>
|
|
153
170
|
<a href={'/nodes/' + node.name} className="link-table">
|
|
154
171
|
{node.name}
|
|
@@ -157,16 +174,16 @@ export function NamespacePage() {
|
|
|
157
174
|
className="rounded-pill badge bg-secondary-soft"
|
|
158
175
|
style={{ marginLeft: '0.5rem' }}
|
|
159
176
|
>
|
|
160
|
-
{node.
|
|
177
|
+
{node.currentVersion}
|
|
161
178
|
</span>
|
|
162
179
|
</td>
|
|
163
180
|
<td>
|
|
164
181
|
<a href={'/nodes/' + node.name} className="link-table">
|
|
165
|
-
{node.type !== 'source' ? node.
|
|
182
|
+
{node.type !== 'source' ? node.current.displayName : ''}
|
|
166
183
|
</a>
|
|
167
184
|
</td>
|
|
168
185
|
<td>
|
|
169
|
-
<span className={'node_type__' + node.type + ' badge node_type'}>
|
|
186
|
+
<span className={'node_type__' + node.type.toLowerCase() + ' badge node_type'}>
|
|
170
187
|
{node.type}
|
|
171
188
|
</span>
|
|
172
189
|
</td>
|
|
@@ -175,7 +192,7 @@ export function NamespacePage() {
|
|
|
175
192
|
</td>
|
|
176
193
|
<td>
|
|
177
194
|
<span className="status">
|
|
178
|
-
{new Date(node.
|
|
195
|
+
{new Date(node.current.updatedAt).toLocaleString('en-us')}
|
|
179
196
|
</span>
|
|
180
197
|
</td>
|
|
181
198
|
<td>
|
|
@@ -184,9 +201,22 @@ export function NamespacePage() {
|
|
|
184
201
|
</tr>
|
|
185
202
|
))
|
|
186
203
|
) : (
|
|
187
|
-
<
|
|
188
|
-
<
|
|
189
|
-
|
|
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
|
+
)
|
|
212
|
+
) : (
|
|
213
|
+
<tr>
|
|
214
|
+
<td>
|
|
215
|
+
<span style={{ display: 'block', marginTop: '2rem' }}>
|
|
216
|
+
<LoadingIcon />
|
|
217
|
+
</span>
|
|
218
|
+
</td>
|
|
219
|
+
</tr>
|
|
190
220
|
);
|
|
191
221
|
|
|
192
222
|
return (
|
|
@@ -194,7 +224,7 @@ export function NamespacePage() {
|
|
|
194
224
|
<div className="card">
|
|
195
225
|
<div className="card-header">
|
|
196
226
|
<h2>Explore</h2>
|
|
197
|
-
<div
|
|
227
|
+
<div className="menu" style={{ margin: '0 0 20px 0' }}>
|
|
198
228
|
<div
|
|
199
229
|
className="menu-link"
|
|
200
230
|
style={{
|
|
@@ -260,6 +290,7 @@ export function NamespacePage() {
|
|
|
260
290
|
item={child}
|
|
261
291
|
current={state.namespace}
|
|
262
292
|
defaultExpand={true}
|
|
293
|
+
key={child.namespace}
|
|
263
294
|
/>
|
|
264
295
|
))
|
|
265
296
|
: null}
|
|
@@ -269,13 +300,13 @@ export function NamespacePage() {
|
|
|
269
300
|
<tr>
|
|
270
301
|
{fields.map(field => {
|
|
271
302
|
return (
|
|
272
|
-
<th>
|
|
303
|
+
<th key={field}>
|
|
273
304
|
<button
|
|
274
305
|
type="button"
|
|
275
306
|
onClick={() => requestSort(field)}
|
|
276
307
|
className={'sortable ' + getClassNamesFor(field)}
|
|
277
308
|
>
|
|
278
|
-
{field.replace(
|
|
309
|
+
{field.replace(/([a-z](?=[A-Z]))/g, '$1 ')}
|
|
279
310
|
</button>
|
|
280
311
|
</th>
|
|
281
312
|
);
|
|
@@ -284,6 +315,14 @@ export function NamespacePage() {
|
|
|
284
315
|
</tr>
|
|
285
316
|
</thead>
|
|
286
317
|
<tbody>{nodesList}</tbody>
|
|
318
|
+
<tfoot>
|
|
319
|
+
<tr>
|
|
320
|
+
<td>
|
|
321
|
+
{retrieved && hasPrevPage ? <a onClick={loadPrev} className="previous round pagination">← Previous</a> : ''}
|
|
322
|
+
{retrieved && hasNextPage ? <a onClick={loadNext} className="next round pagination">Next →</a> : ''}
|
|
323
|
+
</td>
|
|
324
|
+
</tr>
|
|
325
|
+
</tfoot>
|
|
287
326
|
</table>
|
|
288
327
|
</div>
|
|
289
328
|
</div>
|
|
@@ -81,7 +81,7 @@ export default function NodeStatus({ node, revalidate = true }) {
|
|
|
81
81
|
<>
|
|
82
82
|
{revalidate && validation?.errors?.length > 0 ? (
|
|
83
83
|
displayValidation
|
|
84
|
-
) : validation?.status === 'valid' || node?.status === 'valid' ? (
|
|
84
|
+
) : validation?.status === 'valid' || node?.status === 'valid' || node?.current?.status === 'VALID' ? (
|
|
85
85
|
<span
|
|
86
86
|
className="status__valid status"
|
|
87
87
|
style={{ alignContent: 'center' }}
|
|
@@ -12,12 +12,12 @@ type DocsSites = {
|
|
|
12
12
|
|
|
13
13
|
// Default docs sites if REACT_APP_DOCS_SITES is not defined
|
|
14
14
|
const defaultDocsSites: DocsSites = {
|
|
15
|
-
|
|
15
|
+
'Open-Source': 'https://www.datajunction.io/',
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
// Parse the JSON map from the environment variable or use the default
|
|
19
19
|
const docsSites: DocsSites = process.env.REACT_APP_DOCS_SITES
|
|
20
|
-
? JSON.parse(process.env.REACT_APP_DOCS_SITES as string) as DocsSites
|
|
20
|
+
? (JSON.parse(process.env.REACT_APP_DOCS_SITES as string) as DocsSites)
|
|
21
21
|
: defaultDocsSites;
|
|
22
22
|
|
|
23
23
|
export function Root() {
|
|
@@ -32,7 +32,10 @@ export function Root() {
|
|
|
32
32
|
<>
|
|
33
33
|
<Helmet>
|
|
34
34
|
<title>DataJunction</title>
|
|
35
|
-
<meta
|
|
35
|
+
<meta
|
|
36
|
+
name="description"
|
|
37
|
+
content="DataJunction Metrics Platform Webapp"
|
|
38
|
+
/>
|
|
36
39
|
</Helmet>
|
|
37
40
|
<div className="container d-flex align-items-center justify-content-between">
|
|
38
41
|
<div className="header">
|
|
@@ -76,10 +79,18 @@ export function Root() {
|
|
|
76
79
|
>
|
|
77
80
|
Docs
|
|
78
81
|
</a>
|
|
79
|
-
<ul
|
|
82
|
+
<ul
|
|
83
|
+
className="dropdown-menu"
|
|
84
|
+
aria-labelledby="docsDropdown"
|
|
85
|
+
>
|
|
80
86
|
{Object.entries(docsSites).map(([key, value]) => (
|
|
81
87
|
<li key={key}>
|
|
82
|
-
<a
|
|
88
|
+
<a
|
|
89
|
+
className="dropdown-item"
|
|
90
|
+
href={value}
|
|
91
|
+
target="_blank"
|
|
92
|
+
rel="noreferrer"
|
|
93
|
+
>
|
|
83
94
|
{key}
|
|
84
95
|
</a>
|
|
85
96
|
</li>
|
|
@@ -106,4 +117,4 @@ export function Root() {
|
|
|
106
117
|
<Outlet />
|
|
107
118
|
</>
|
|
108
119
|
);
|
|
109
|
-
}
|
|
120
|
+
}
|
|
@@ -4,7 +4,84 @@ const DJ_URL = process.env.REACT_APP_DJ_URL
|
|
|
4
4
|
? process.env.REACT_APP_DJ_URL
|
|
5
5
|
: 'http://localhost:8000';
|
|
6
6
|
|
|
7
|
+
const DJ_GQL = process.env.REACT_APP_DJ_GQL
|
|
8
|
+
? process.env.REACT_APP_DJ_GQL
|
|
9
|
+
: 'http://localhost:8000/graphql';
|
|
10
|
+
|
|
7
11
|
export const DataJunctionAPI = {
|
|
12
|
+
listNodesForLanding: async function (
|
|
13
|
+
namespace,
|
|
14
|
+
nodeTypes,
|
|
15
|
+
tags,
|
|
16
|
+
editedBy,
|
|
17
|
+
before,
|
|
18
|
+
after,
|
|
19
|
+
limit,
|
|
20
|
+
) {
|
|
21
|
+
const query = `
|
|
22
|
+
query ListNodes($namespace: String, $nodeTypes: [NodeType!], $tags: [String!], $editedBy: String, $before: String, $after: String, $limit: Int) {
|
|
23
|
+
findNodesPaginated(
|
|
24
|
+
namespace: $namespace
|
|
25
|
+
nodeTypes: $nodeTypes
|
|
26
|
+
tags: $tags
|
|
27
|
+
editedBy: $editedBy
|
|
28
|
+
limit: $limit
|
|
29
|
+
before: $before
|
|
30
|
+
after: $after
|
|
31
|
+
) {
|
|
32
|
+
pageInfo {
|
|
33
|
+
hasNextPage
|
|
34
|
+
endCursor
|
|
35
|
+
hasPrevPage
|
|
36
|
+
startCursor
|
|
37
|
+
}
|
|
38
|
+
edges {
|
|
39
|
+
node {
|
|
40
|
+
name
|
|
41
|
+
type
|
|
42
|
+
currentVersion
|
|
43
|
+
tags {
|
|
44
|
+
name
|
|
45
|
+
tagType
|
|
46
|
+
}
|
|
47
|
+
editedBy
|
|
48
|
+
current {
|
|
49
|
+
displayName
|
|
50
|
+
status
|
|
51
|
+
updatedAt
|
|
52
|
+
}
|
|
53
|
+
createdBy {
|
|
54
|
+
username
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
return await (
|
|
63
|
+
await fetch(DJ_GQL, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: {
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
},
|
|
68
|
+
credentials: 'include',
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
query,
|
|
71
|
+
variables: {
|
|
72
|
+
namespace: namespace,
|
|
73
|
+
nodeTypes: nodeTypes,
|
|
74
|
+
tags: tags,
|
|
75
|
+
editedBy: editedBy,
|
|
76
|
+
before: before,
|
|
77
|
+
after: after,
|
|
78
|
+
limit: limit,
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
})
|
|
82
|
+
).json();
|
|
83
|
+
},
|
|
84
|
+
|
|
8
85
|
whoami: async function () {
|
|
9
86
|
return await (
|
|
10
87
|
await fetch(`${DJ_URL}/whoami/`, { credentials: 'include' })
|
|
@@ -346,11 +423,14 @@ export const DataJunctionAPI = {
|
|
|
346
423
|
).json();
|
|
347
424
|
},
|
|
348
425
|
|
|
349
|
-
namespace: async function (nmspce) {
|
|
426
|
+
namespace: async function (nmspce, editedBy) {
|
|
350
427
|
return await (
|
|
351
|
-
await fetch(
|
|
352
|
-
|
|
353
|
-
|
|
428
|
+
await fetch(
|
|
429
|
+
`${DJ_URL}/namespaces/${nmspce}?edited_by=${editedBy}&with_edited_by=true`,
|
|
430
|
+
{
|
|
431
|
+
credentials: 'include',
|
|
432
|
+
},
|
|
433
|
+
)
|
|
354
434
|
).json();
|
|
355
435
|
},
|
|
356
436
|
|
|
@@ -426,7 +426,7 @@ describe('DataJunctionAPI', () => {
|
|
|
426
426
|
const nmspce = 'sampleNamespace';
|
|
427
427
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
428
428
|
await DataJunctionAPI.namespace(nmspce);
|
|
429
|
-
expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/${nmspce}
|
|
429
|
+
expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/namespaces/${nmspce}?edited_by=undefined&with_edited_by=true`, {
|
|
430
430
|
credentials: 'include',
|
|
431
431
|
});
|
|
432
432
|
});
|
|
@@ -1069,4 +1069,20 @@ describe('DataJunctionAPI', () => {
|
|
|
1069
1069
|
},
|
|
1070
1070
|
);
|
|
1071
1071
|
});
|
|
1072
|
+
|
|
1073
|
+
it('calls listNodesForLanding correctly', () => {
|
|
1074
|
+
fetch.mockResponseOnce(JSON.stringify({}));
|
|
1075
|
+
|
|
1076
|
+
DataJunctionAPI.listNodesForLanding('', ['source'], [], '', null, null, 100);
|
|
1077
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
1078
|
+
`${DJ_URL}/graphql`,
|
|
1079
|
+
expect.objectContaining({
|
|
1080
|
+
method: 'POST',
|
|
1081
|
+
credentials: 'include',
|
|
1082
|
+
headers: {
|
|
1083
|
+
'Content-Type': 'application/json',
|
|
1084
|
+
},
|
|
1085
|
+
}),
|
|
1086
|
+
);
|
|
1087
|
+
});
|
|
1072
1088
|
});
|
package/src/styles/index.css
CHANGED
|
@@ -1050,6 +1050,32 @@ pre {
|
|
|
1050
1050
|
white-space: break-spaces;
|
|
1051
1051
|
}
|
|
1052
1052
|
|
|
1053
|
+
.pagination {
|
|
1054
|
+
background-color: #fff !important;
|
|
1055
|
+
color: #74b7c3;
|
|
1056
|
+
border: 1px solid #74b7c3;
|
|
1057
|
+
text-transform: none;
|
|
1058
|
+
vertical-align: middle;
|
|
1059
|
+
padding-right: 1rem;
|
|
1060
|
+
padding-left: 1rem;
|
|
1061
|
+
padding-top: 0.5rem;
|
|
1062
|
+
padding-bottom: 0.5rem;
|
|
1063
|
+
margin-left: 0.5rem !important;
|
|
1064
|
+
margin-bottom: 0.5rem !important;
|
|
1065
|
+
font-size: 1rem;
|
|
1066
|
+
border-radius: 0.2rem;
|
|
1067
|
+
word-wrap: break-word;
|
|
1068
|
+
white-space: break-spaces;
|
|
1069
|
+
text-decoration: none;
|
|
1070
|
+
}
|
|
1071
|
+
.pagination:hover {
|
|
1072
|
+
background-color: #b6dae0 !important;
|
|
1073
|
+
color: #436e76;
|
|
1074
|
+
text-transform: none;
|
|
1075
|
+
text-decoration: none;
|
|
1076
|
+
cursor: pointer;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1053
1079
|
.edit_button {
|
|
1054
1080
|
background: none;
|
|
1055
1081
|
color: inherit;
|