datajunction-ui 0.0.106 → 0.0.108
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/components/NamespaceHeader.jsx +5 -0
- package/src/app/pages/NamespacePage/Explorer.jsx +36 -45
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +13 -34
- package/src/app/pages/NamespacePage/index.jsx +73 -19
- package/src/app/services/DJService.js +38 -0
package/package.json
CHANGED
|
@@ -45,6 +45,11 @@ export default function NamespaceHeader({
|
|
|
45
45
|
setExistingPR(null);
|
|
46
46
|
|
|
47
47
|
const fetchData = async () => {
|
|
48
|
+
if (!namespace) {
|
|
49
|
+
if (onGitConfigLoaded) onGitConfigLoaded(null);
|
|
50
|
+
setGitConfigLoading(false);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
48
53
|
if (namespace) {
|
|
49
54
|
// Fetch deployment sources
|
|
50
55
|
try {
|
|
@@ -8,7 +8,7 @@ const Explorer = ({
|
|
|
8
8
|
item = [],
|
|
9
9
|
current,
|
|
10
10
|
isTopLevel = false,
|
|
11
|
-
|
|
11
|
+
gitRoots = new Set(),
|
|
12
12
|
}) => {
|
|
13
13
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
14
14
|
const [items, setItems] = useState([]);
|
|
@@ -139,50 +139,41 @@ const Explorer = ({
|
|
|
139
139
|
>
|
|
140
140
|
{items.namespace}
|
|
141
141
|
</a>
|
|
142
|
-
{/*
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
142
|
+
{/* Git root badge */}
|
|
143
|
+
{gitRoots.has(items.path) && (
|
|
144
|
+
<span
|
|
145
|
+
title="Git-backed namespace"
|
|
146
|
+
style={{
|
|
147
|
+
marginLeft: '6px',
|
|
148
|
+
fontSize: '9px',
|
|
149
|
+
padding: '1px 4px',
|
|
150
|
+
borderRadius: '3px',
|
|
151
|
+
backgroundColor: '#d4edda',
|
|
152
|
+
color: '#155724',
|
|
153
|
+
display: 'inline-flex',
|
|
154
|
+
alignItems: 'center',
|
|
155
|
+
gap: '2px',
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
<svg
|
|
159
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
160
|
+
width="10"
|
|
161
|
+
height="10"
|
|
162
|
+
viewBox="0 0 24 24"
|
|
163
|
+
fill="none"
|
|
164
|
+
stroke="currentColor"
|
|
165
|
+
strokeWidth="2"
|
|
166
|
+
strokeLinecap="round"
|
|
167
|
+
strokeLinejoin="round"
|
|
166
168
|
>
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
strokeLinecap="round"
|
|
176
|
-
strokeLinejoin="round"
|
|
177
|
-
>
|
|
178
|
-
<line x1="6" y1="3" x2="6" y2="15"></line>
|
|
179
|
-
<circle cx="18" cy="6" r="3"></circle>
|
|
180
|
-
<circle cx="6" cy="18" r="3"></circle>
|
|
181
|
-
<path d="M18 9a9 9 0 0 1-9 9"></path>
|
|
182
|
-
</svg>
|
|
183
|
-
Git
|
|
184
|
-
</span>
|
|
185
|
-
)}
|
|
169
|
+
<line x1="6" y1="3" x2="6" y2="15"></line>
|
|
170
|
+
<circle cx="18" cy="6" r="3"></circle>
|
|
171
|
+
<circle cx="6" cy="18" r="3"></circle>
|
|
172
|
+
<path d="M18 9a9 9 0 0 1-9 9"></path>
|
|
173
|
+
</svg>
|
|
174
|
+
Git
|
|
175
|
+
</span>
|
|
176
|
+
)}
|
|
186
177
|
<button
|
|
187
178
|
className="namespace-add-button"
|
|
188
179
|
onClick={e => {
|
|
@@ -289,7 +280,7 @@ const Explorer = ({
|
|
|
289
280
|
item={item}
|
|
290
281
|
current={highlight}
|
|
291
282
|
isTopLevel={false}
|
|
292
|
-
|
|
283
|
+
gitRoots={gitRoots}
|
|
293
284
|
/>
|
|
294
285
|
</div>
|
|
295
286
|
</div>
|
|
@@ -8,6 +8,7 @@ import userEvent from '@testing-library/user-event';
|
|
|
8
8
|
|
|
9
9
|
const mockDjClient = {
|
|
10
10
|
namespaces: jest.fn(),
|
|
11
|
+
listNamespacesWithGit: jest.fn(),
|
|
11
12
|
namespace: jest.fn(),
|
|
12
13
|
listNodesForLanding: jest.fn(),
|
|
13
14
|
addNamespace: jest.fn(),
|
|
@@ -81,40 +82,18 @@ describe('NamespacePage', () => {
|
|
|
81
82
|
mockDjClient.getNamespaceBranches.mockResolvedValue([]);
|
|
82
83
|
mockDjClient.listDeployments.mockResolvedValue([]);
|
|
83
84
|
mockDjClient.getPullRequest.mockResolvedValue(null);
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
namespace: 'common.one.c',
|
|
99
|
-
num_nodes: 64,
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
namespace: 'default',
|
|
103
|
-
num_nodes: 41,
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
namespace: 'default.fruits',
|
|
107
|
-
num_nodes: 1,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
namespace: 'default.fruits.citrus.lemons',
|
|
111
|
-
num_nodes: 1,
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
namespace: 'default.vegetables',
|
|
115
|
-
num_nodes: 2,
|
|
116
|
-
},
|
|
117
|
-
]);
|
|
85
|
+
const mockNamespaces = [
|
|
86
|
+
{ namespace: 'common.one', numNodes: 3, git: null },
|
|
87
|
+
{ namespace: 'common.one.a', numNodes: 6, git: null },
|
|
88
|
+
{ namespace: 'common.one.b', numNodes: 17, git: null },
|
|
89
|
+
{ namespace: 'common.one.c', numNodes: 64, git: null },
|
|
90
|
+
{ namespace: 'default', numNodes: 41, git: null },
|
|
91
|
+
{ namespace: 'default.fruits', numNodes: 1, git: null },
|
|
92
|
+
{ namespace: 'default.fruits.citrus.lemons', numNodes: 1, git: null },
|
|
93
|
+
{ namespace: 'default.vegetables', numNodes: 2, git: null },
|
|
94
|
+
];
|
|
95
|
+
mockDjClient.namespaces.mockResolvedValue(mockNamespaces);
|
|
96
|
+
mockDjClient.listNamespacesWithGit.mockResolvedValue(mockNamespaces);
|
|
118
97
|
mockDjClient.namespace.mockResolvedValue([
|
|
119
98
|
{
|
|
120
99
|
name: 'testNode',
|
|
@@ -219,7 +219,15 @@ export function NamespacePage() {
|
|
|
219
219
|
const ASC = 'ascending';
|
|
220
220
|
const DESC = 'descending';
|
|
221
221
|
|
|
222
|
-
const fields = [
|
|
222
|
+
const fields = [
|
|
223
|
+
'name',
|
|
224
|
+
'displayName',
|
|
225
|
+
'type',
|
|
226
|
+
'status',
|
|
227
|
+
'mode',
|
|
228
|
+
'owners',
|
|
229
|
+
'updatedAt',
|
|
230
|
+
];
|
|
223
231
|
|
|
224
232
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
225
233
|
const { currentUser } = useCurrentUser();
|
|
@@ -368,7 +376,7 @@ export function NamespacePage() {
|
|
|
368
376
|
const [retrieved, setRetrieved] = useState(false);
|
|
369
377
|
|
|
370
378
|
const [namespaceHierarchy, setNamespaceHierarchy] = useState([]);
|
|
371
|
-
const [
|
|
379
|
+
const [gitRoots, setGitRoots] = useState(new Set());
|
|
372
380
|
// Use undefined to indicate "not yet loaded", null means "loaded but no config"
|
|
373
381
|
const [gitConfig, setGitConfig] = useState(undefined);
|
|
374
382
|
|
|
@@ -486,6 +494,7 @@ export function NamespacePage() {
|
|
|
486
494
|
path: path,
|
|
487
495
|
};
|
|
488
496
|
currentLevel.push(existingNamespace);
|
|
497
|
+
currentLevel.sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
489
498
|
}
|
|
490
499
|
|
|
491
500
|
currentLevel = existingNamespace.children;
|
|
@@ -497,23 +506,19 @@ export function NamespacePage() {
|
|
|
497
506
|
|
|
498
507
|
useEffect(() => {
|
|
499
508
|
const fetchData = async () => {
|
|
500
|
-
const namespaces = await djClient.
|
|
509
|
+
const namespaces = await djClient.listNamespacesWithGit();
|
|
501
510
|
const hierarchy = createNamespaceHierarchy(namespaces);
|
|
502
511
|
setNamespaceHierarchy(hierarchy);
|
|
503
512
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (sourcesResponse && sourcesResponse.sources) {
|
|
511
|
-
setNamespaceSources(sourcesResponse.sources);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
513
|
+
const roots = new Set(
|
|
514
|
+
namespaces
|
|
515
|
+
.filter(ns => ns.git?.__typename === 'GitRootConfig')
|
|
516
|
+
.map(ns => ns.namespace),
|
|
517
|
+
);
|
|
518
|
+
setGitRoots(roots);
|
|
514
519
|
};
|
|
515
520
|
fetchData().catch(console.error);
|
|
516
|
-
}, [djClient
|
|
521
|
+
}, [djClient]);
|
|
517
522
|
|
|
518
523
|
useEffect(() => {
|
|
519
524
|
const fetchData = async () => {
|
|
@@ -620,7 +625,14 @@ export function NamespacePage() {
|
|
|
620
625
|
state.nodes.length > 0 ? (
|
|
621
626
|
state.nodes.map(node => (
|
|
622
627
|
<tr key={node.name}>
|
|
623
|
-
<td
|
|
628
|
+
<td
|
|
629
|
+
style={{
|
|
630
|
+
maxWidth: '300px',
|
|
631
|
+
overflow: 'hidden',
|
|
632
|
+
whiteSpace: 'nowrap',
|
|
633
|
+
textOverflow: 'ellipsis',
|
|
634
|
+
}}
|
|
635
|
+
>
|
|
624
636
|
<a href={'/nodes/' + node.name} className="link-table">
|
|
625
637
|
{isBranchNamespace && node.name.startsWith(namespace + '.')
|
|
626
638
|
? node.name.slice(namespace.length + 1)
|
|
@@ -633,7 +645,14 @@ export function NamespacePage() {
|
|
|
633
645
|
{node.currentVersion}
|
|
634
646
|
</span>
|
|
635
647
|
</td>
|
|
636
|
-
<td
|
|
648
|
+
<td
|
|
649
|
+
style={{
|
|
650
|
+
maxWidth: '250px',
|
|
651
|
+
overflow: 'hidden',
|
|
652
|
+
whiteSpace: 'nowrap',
|
|
653
|
+
textOverflow: 'ellipsis',
|
|
654
|
+
}}
|
|
655
|
+
>
|
|
637
656
|
<a href={'/nodes/' + node.name} className="link-table">
|
|
638
657
|
{node.type !== 'source' ? node.current.displayName : ''}
|
|
639
658
|
</a>
|
|
@@ -673,9 +692,44 @@ export function NamespacePage() {
|
|
|
673
692
|
{node.current.mode === 'PUBLISHED' ? 'P' : 'D'}
|
|
674
693
|
</span>
|
|
675
694
|
</td>
|
|
695
|
+
<td>
|
|
696
|
+
{node.owners?.length > 0 && (
|
|
697
|
+
<div style={{ display: 'flex', gap: '2px' }}>
|
|
698
|
+
{node.owners.slice(0, 3).map(owner => {
|
|
699
|
+
const initials = owner.username
|
|
700
|
+
.split('@')[0]
|
|
701
|
+
.slice(0, 2)
|
|
702
|
+
.toUpperCase();
|
|
703
|
+
const [bg, fg] =
|
|
704
|
+
AVATAR_COLORS[avatarColorIndex(owner.username)];
|
|
705
|
+
return (
|
|
706
|
+
<span
|
|
707
|
+
key={owner.username}
|
|
708
|
+
title={owner.username}
|
|
709
|
+
style={{
|
|
710
|
+
display: 'inline-flex',
|
|
711
|
+
alignItems: 'center',
|
|
712
|
+
justifyContent: 'center',
|
|
713
|
+
width: '24px',
|
|
714
|
+
height: '24px',
|
|
715
|
+
borderRadius: '50%',
|
|
716
|
+
backgroundColor: bg,
|
|
717
|
+
color: fg,
|
|
718
|
+
fontSize: '9px',
|
|
719
|
+
fontWeight: '600',
|
|
720
|
+
flexShrink: 0,
|
|
721
|
+
}}
|
|
722
|
+
>
|
|
723
|
+
{initials}
|
|
724
|
+
</span>
|
|
725
|
+
);
|
|
726
|
+
})}
|
|
727
|
+
</div>
|
|
728
|
+
)}
|
|
729
|
+
</td>
|
|
676
730
|
<td>
|
|
677
731
|
<span className="status">
|
|
678
|
-
{new Date(node.current.updatedAt).
|
|
732
|
+
{new Date(node.current.updatedAt).toLocaleDateString('en-us')}
|
|
679
733
|
</span>
|
|
680
734
|
</td>
|
|
681
735
|
{showEditControls && (
|
|
@@ -687,7 +741,7 @@ export function NamespacePage() {
|
|
|
687
741
|
))
|
|
688
742
|
) : (
|
|
689
743
|
<tr>
|
|
690
|
-
<td colSpan={
|
|
744
|
+
<td colSpan={8}>
|
|
691
745
|
<span
|
|
692
746
|
style={{
|
|
693
747
|
display: 'block',
|
|
@@ -1075,7 +1129,7 @@ export function NamespacePage() {
|
|
|
1075
1129
|
defaultExpand={true}
|
|
1076
1130
|
isTopLevel={true}
|
|
1077
1131
|
key={child.namespace}
|
|
1078
|
-
|
|
1132
|
+
gitRoots={gitRoots}
|
|
1079
1133
|
/>
|
|
1080
1134
|
))
|
|
1081
1135
|
: null}
|
|
@@ -1123,6 +1123,44 @@ export const DataJunctionAPI = {
|
|
|
1123
1123
|
).json();
|
|
1124
1124
|
},
|
|
1125
1125
|
|
|
1126
|
+
listNamespacesWithGit: async function () {
|
|
1127
|
+
const query = `
|
|
1128
|
+
query ListNamespaces {
|
|
1129
|
+
listNamespaces {
|
|
1130
|
+
namespace
|
|
1131
|
+
numNodes
|
|
1132
|
+
git {
|
|
1133
|
+
__typename
|
|
1134
|
+
... on GitRootConfig {
|
|
1135
|
+
repo
|
|
1136
|
+
path
|
|
1137
|
+
defaultBranch
|
|
1138
|
+
}
|
|
1139
|
+
... on GitBranchConfig {
|
|
1140
|
+
branch
|
|
1141
|
+
gitOnly
|
|
1142
|
+
parentNamespace
|
|
1143
|
+
root {
|
|
1144
|
+
repo
|
|
1145
|
+
path
|
|
1146
|
+
defaultBranch
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
`;
|
|
1153
|
+
const result = await (
|
|
1154
|
+
await fetch(DJ_GQL, {
|
|
1155
|
+
method: 'POST',
|
|
1156
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1157
|
+
credentials: 'include',
|
|
1158
|
+
body: JSON.stringify({ query }),
|
|
1159
|
+
})
|
|
1160
|
+
).json();
|
|
1161
|
+
return result?.data?.listNamespaces || [];
|
|
1162
|
+
},
|
|
1163
|
+
|
|
1126
1164
|
namespaceSources: async function (namespace) {
|
|
1127
1165
|
return await (
|
|
1128
1166
|
await fetch(`${DJ_URL}/namespaces/${namespace}/sources`, {
|