@finos/legend-application-studio 28.19.69 → 28.19.70
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/lib/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.js +338 -26
- package/lib/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/EditorGraphState.d.ts.map +1 -1
- package/lib/stores/editor/EditorGraphState.js +7 -1
- package/lib/stores/editor/EditorGraphState.js.map +1 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.d.ts +2 -0
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js +19 -0
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js.map +1 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.d.ts +16 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.js +112 -1
- package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.js.map +1 -1
- package/package.json +5 -5
- package/src/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.tsx +599 -53
- package/src/stores/editor/EditorGraphState.ts +11 -4
- package/src/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.ts +26 -0
- package/src/stores/editor/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.ts +178 -1
package/src/components/editor/editor-group/project-configuration-editor/ProjectDependencyEditor.tsx
CHANGED
|
@@ -61,7 +61,10 @@ import {
|
|
|
61
61
|
type StoreProjectData,
|
|
62
62
|
SNAPSHOT_VERSION_ALIAS,
|
|
63
63
|
} from '@finos/legend-server-depot';
|
|
64
|
-
import
|
|
64
|
+
import {
|
|
65
|
+
type ProjectDependency,
|
|
66
|
+
type ProjectDependencyExclusion,
|
|
67
|
+
} from '@finos/legend-server-sdlc';
|
|
65
68
|
import {
|
|
66
69
|
ActionState,
|
|
67
70
|
assertErrorThrown,
|
|
@@ -74,7 +77,7 @@ import {
|
|
|
74
77
|
import { generateGAVCoordinates } from '@finos/legend-storage';
|
|
75
78
|
import { flowResult } from 'mobx';
|
|
76
79
|
import { observer } from 'mobx-react-lite';
|
|
77
|
-
import { forwardRef, useEffect, useRef, useState } from 'react';
|
|
80
|
+
import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
|
|
78
81
|
import { ProjectConfigurationEditorState } from '../../../../stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js';
|
|
79
82
|
import {
|
|
80
83
|
type ProjectDependencyConflictTreeNodeData,
|
|
@@ -250,9 +253,117 @@ const DependencyTreeNodeContainer: React.FC<
|
|
|
250
253
|
>
|
|
251
254
|
> = (props) => {
|
|
252
255
|
const { node, level, stepPaddingInRem, onNodeSelect } = props;
|
|
256
|
+
const editorStore = useEditorStore();
|
|
257
|
+
const dependencyEditorState =
|
|
258
|
+
editorStore.projectConfigurationEditorState.projectDependencyEditorState;
|
|
259
|
+
|
|
253
260
|
const isExpandable = Boolean(node.childrenIds?.length);
|
|
254
261
|
const selectNode = (): void => onNodeSelect?.(node);
|
|
255
262
|
const value = node.value;
|
|
263
|
+
|
|
264
|
+
const isExcluded = (): boolean => {
|
|
265
|
+
const coordinate = generateGAVCoordinates(
|
|
266
|
+
value.groupId,
|
|
267
|
+
value.artifactId,
|
|
268
|
+
undefined,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const treeData = dependencyEditorState.dependencyTreeData;
|
|
272
|
+
if (!treeData) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const findRootNodeForCurrentNode = (nodeId: string): string | null => {
|
|
277
|
+
if (treeData.rootIds.indexOf(nodeId) !== -1) {
|
|
278
|
+
return nodeId;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const findInSubtree = (rootId: string, searchId: string): boolean => {
|
|
282
|
+
const rootNode = treeData.nodes.get(rootId);
|
|
283
|
+
if (!rootNode) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
const visited: { [key: string]: boolean } = {};
|
|
287
|
+
const queue = [rootId];
|
|
288
|
+
|
|
289
|
+
while (queue.length > 0) {
|
|
290
|
+
const currentId = queue.shift();
|
|
291
|
+
if (!currentId || visited[currentId]) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
visited[currentId] = true;
|
|
296
|
+
|
|
297
|
+
if (currentId === searchId) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const currentNode = treeData.nodes.get(currentId);
|
|
302
|
+
if (currentNode?.childrenIds) {
|
|
303
|
+
for (let i = 0; i < currentNode.childrenIds.length; i++) {
|
|
304
|
+
queue.push(guaranteeNonNullable(currentNode.childrenIds[i]));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
for (let i = 0; i < treeData.rootIds.length; i++) {
|
|
312
|
+
const rootId = guaranteeNonNullable(treeData.rootIds[i]);
|
|
313
|
+
if (findInSubtree(rootId, nodeId)) {
|
|
314
|
+
return rootId;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return null;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const rootNodeId = findRootNodeForCurrentNode(node.id);
|
|
322
|
+
if (!rootNodeId) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const rootNode = treeData.nodes.get(rootNodeId);
|
|
327
|
+
if (!rootNode) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const currentProjectConfiguration =
|
|
332
|
+
editorStore.projectConfigurationEditorState.currentProjectConfiguration;
|
|
333
|
+
const rootCoordinate = `${rootNode.value.groupId}:${rootNode.value.artifactId}`;
|
|
334
|
+
|
|
335
|
+
for (
|
|
336
|
+
let i = 0;
|
|
337
|
+
i < currentProjectConfiguration.projectDependencies.length;
|
|
338
|
+
i++
|
|
339
|
+
) {
|
|
340
|
+
const projectDep = guaranteeNonNullable(
|
|
341
|
+
currentProjectConfiguration.projectDependencies[i],
|
|
342
|
+
);
|
|
343
|
+
const projectDepCoordinate = generateGAVCoordinates(
|
|
344
|
+
guaranteeNonNullable(projectDep.groupId),
|
|
345
|
+
guaranteeNonNullable(projectDep.artifactId),
|
|
346
|
+
undefined,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
if (projectDepCoordinate === rootCoordinate) {
|
|
350
|
+
const exclusions = dependencyEditorState.getExclusions(
|
|
351
|
+
projectDep.projectId,
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
for (let j = 0; j < exclusions.length; j++) {
|
|
355
|
+
if (guaranteeNonNullable(exclusions[j]).coordinate === coordinate) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return false;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const nodeIsExcluded = isExcluded();
|
|
256
367
|
const nodeExpandIcon = isExpandable ? (
|
|
257
368
|
node.isOpen ? (
|
|
258
369
|
<ChevronDownIcon />
|
|
@@ -277,6 +388,10 @@ const DependencyTreeNodeContainer: React.FC<
|
|
|
277
388
|
'project-dependency-explorer-tree__node__container--selected':
|
|
278
389
|
node.isSelected,
|
|
279
390
|
},
|
|
391
|
+
{
|
|
392
|
+
'project-dependency-explorer-tree__node__container--excluded':
|
|
393
|
+
nodeIsExcluded,
|
|
394
|
+
},
|
|
280
395
|
)}
|
|
281
396
|
style={{
|
|
282
397
|
paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
|
|
@@ -289,15 +404,27 @@ const DependencyTreeNodeContainer: React.FC<
|
|
|
289
404
|
</div>
|
|
290
405
|
</div>
|
|
291
406
|
<button
|
|
292
|
-
className=
|
|
407
|
+
className={clsx(
|
|
408
|
+
'tree-view__node__label project-dependency-explorer-tree__node__label',
|
|
409
|
+
{
|
|
410
|
+
'project-dependency-explorer-tree__node__label--excluded':
|
|
411
|
+
nodeIsExcluded,
|
|
412
|
+
},
|
|
413
|
+
)}
|
|
293
414
|
tabIndex={-1}
|
|
294
|
-
title={value.id}
|
|
415
|
+
title={nodeIsExcluded ? `${value.id} (EXCLUDED)` : value.id}
|
|
295
416
|
>
|
|
296
417
|
{value.artifactId}
|
|
297
418
|
</button>
|
|
298
419
|
<div className="project-dependency-explorer-tree__node__version">
|
|
299
420
|
<button
|
|
300
|
-
className=
|
|
421
|
+
className={clsx(
|
|
422
|
+
'project-dependency-explorer-tree__node__version-btn',
|
|
423
|
+
{
|
|
424
|
+
'project-dependency-explorer-tree__node__version-btn--excluded':
|
|
425
|
+
nodeIsExcluded,
|
|
426
|
+
},
|
|
427
|
+
)}
|
|
301
428
|
title={value.versionId}
|
|
302
429
|
tabIndex={-1}
|
|
303
430
|
>
|
|
@@ -364,8 +491,114 @@ const ConflictTreeNodeContainer: React.FC<
|
|
|
364
491
|
>
|
|
365
492
|
> = (props) => {
|
|
366
493
|
const { node, level, stepPaddingInRem, onNodeSelect } = props;
|
|
494
|
+
const editorStore = useEditorStore();
|
|
495
|
+
const dependencyEditorState =
|
|
496
|
+
editorStore.projectConfigurationEditorState.projectDependencyEditorState;
|
|
497
|
+
|
|
367
498
|
const isExpandable = Boolean(node.childrenIds?.length);
|
|
368
499
|
const selectNode = (): void => onNodeSelect?.(node);
|
|
500
|
+
|
|
501
|
+
const isExcluded = (): boolean => {
|
|
502
|
+
if (!(node instanceof ProjectDependencyTreeNodeData)) {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
const value = node.value;
|
|
507
|
+
const coordinate = `${value.groupId}:${value.artifactId}`;
|
|
508
|
+
|
|
509
|
+
const treeData = dependencyEditorState.dependencyTreeData;
|
|
510
|
+
if (!treeData) {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const findRootNodeForCurrentNode = (nodeId: string): string | null => {
|
|
515
|
+
if (treeData.rootIds.indexOf(nodeId) !== -1) {
|
|
516
|
+
return nodeId;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const findInSubtree = (rootId: string, searchId: string): boolean => {
|
|
520
|
+
const rootNode = treeData.nodes.get(rootId);
|
|
521
|
+
if (!rootNode) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
const visited: { [key: string]: boolean } = {};
|
|
525
|
+
const queue = [rootId];
|
|
526
|
+
|
|
527
|
+
while (queue.length > 0) {
|
|
528
|
+
const currentId = queue.shift();
|
|
529
|
+
if (!currentId || visited[currentId]) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
visited[currentId] = true;
|
|
533
|
+
|
|
534
|
+
if (currentId === searchId) {
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const currentNode = treeData.nodes.get(currentId);
|
|
539
|
+
if (currentNode?.childrenIds) {
|
|
540
|
+
for (let i = 0; i < currentNode.childrenIds.length; i++) {
|
|
541
|
+
queue.push(guaranteeNonNullable(currentNode.childrenIds[i]));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return false;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
for (let i = 0; i < treeData.rootIds.length; i++) {
|
|
549
|
+
const rootId = guaranteeNonNullable(treeData.rootIds[i]);
|
|
550
|
+
if (findInSubtree(rootId, nodeId)) {
|
|
551
|
+
return rootId;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return null;
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
const rootNodeId = findRootNodeForCurrentNode(node.id);
|
|
559
|
+
if (!rootNodeId) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const rootNode = treeData.nodes.get(rootNodeId);
|
|
564
|
+
if (!rootNode) {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const currentProjectConfiguration =
|
|
569
|
+
editorStore.projectConfigurationEditorState.currentProjectConfiguration;
|
|
570
|
+
const rootCoordinate = `${rootNode.value.groupId}:${rootNode.value.artifactId}`;
|
|
571
|
+
|
|
572
|
+
for (
|
|
573
|
+
let i = 0;
|
|
574
|
+
i < currentProjectConfiguration.projectDependencies.length;
|
|
575
|
+
i++
|
|
576
|
+
) {
|
|
577
|
+
const projectDep = guaranteeNonNullable(
|
|
578
|
+
currentProjectConfiguration.projectDependencies[i],
|
|
579
|
+
);
|
|
580
|
+
const projectDepCoordinate = generateGAVCoordinates(
|
|
581
|
+
guaranteeNonNullable(projectDep.groupId),
|
|
582
|
+
guaranteeNonNullable(projectDep.artifactId),
|
|
583
|
+
undefined,
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
if (projectDepCoordinate === rootCoordinate) {
|
|
587
|
+
const exclusions = dependencyEditorState.getExclusions(
|
|
588
|
+
projectDep.projectId,
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
for (let j = 0; j < exclusions.length; j++) {
|
|
592
|
+
if (guaranteeNonNullable(exclusions[j]).coordinate === coordinate) {
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return false;
|
|
601
|
+
};
|
|
369
602
|
const nodeExpandIcon = isExpandable ? (
|
|
370
603
|
node.isOpen ? (
|
|
371
604
|
<ChevronDownIcon />
|
|
@@ -390,6 +623,10 @@ const ConflictTreeNodeContainer: React.FC<
|
|
|
390
623
|
'project-dependency-explorer-tree__node__container--selected':
|
|
391
624
|
node.isSelected,
|
|
392
625
|
},
|
|
626
|
+
{
|
|
627
|
+
'project-dependency-explorer-tree__node__container--excluded':
|
|
628
|
+
isExcluded(),
|
|
629
|
+
},
|
|
393
630
|
)}
|
|
394
631
|
style={{
|
|
395
632
|
paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
|
|
@@ -421,17 +658,35 @@ const ConflictTreeNodeContainer: React.FC<
|
|
|
421
658
|
)}
|
|
422
659
|
</div>
|
|
423
660
|
<button
|
|
424
|
-
className=
|
|
661
|
+
className={clsx(
|
|
662
|
+
'tree-view__node__label project-dependency-explorer-tree__node__label',
|
|
663
|
+
{
|
|
664
|
+
'project-dependency-explorer-tree__node__label--excluded':
|
|
665
|
+
isExcluded(),
|
|
666
|
+
},
|
|
667
|
+
)}
|
|
425
668
|
tabIndex={-1}
|
|
426
|
-
title={
|
|
669
|
+
title={
|
|
670
|
+
isExcluded() ? `${node.description} (EXCLUDED)` : node.description
|
|
671
|
+
}
|
|
427
672
|
>
|
|
428
673
|
{node.label}
|
|
429
674
|
</button>
|
|
430
675
|
{node instanceof ProjectDependencyTreeNodeData && (
|
|
431
676
|
<div className="project-dependency-explorer-tree__node__version">
|
|
432
677
|
<button
|
|
433
|
-
className=
|
|
434
|
-
|
|
678
|
+
className={clsx(
|
|
679
|
+
'project-dependency-explorer-tree__node__version-btn',
|
|
680
|
+
{
|
|
681
|
+
'project-dependency-explorer-tree__node__version-btn--excluded':
|
|
682
|
+
isExcluded(),
|
|
683
|
+
},
|
|
684
|
+
)}
|
|
685
|
+
title={
|
|
686
|
+
isExcluded()
|
|
687
|
+
? `${node.value.versionId} (EXCLUDED)`
|
|
688
|
+
: node.value.versionId
|
|
689
|
+
}
|
|
435
690
|
tabIndex={-1}
|
|
436
691
|
>
|
|
437
692
|
{node.value.versionId}
|
|
@@ -747,6 +1002,267 @@ const ProjectDependencyReportModal = observer(
|
|
|
747
1002
|
},
|
|
748
1003
|
);
|
|
749
1004
|
|
|
1005
|
+
interface TransitiveDependencyOption {
|
|
1006
|
+
label: string;
|
|
1007
|
+
value: string;
|
|
1008
|
+
groupId: string;
|
|
1009
|
+
artifactId: string;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const ProjectDependencyInlineExclusionsSelector = observer(
|
|
1013
|
+
(props: { projectDependency: ProjectDependency; isReadOnly: boolean }) => {
|
|
1014
|
+
const { projectDependency, isReadOnly } = props;
|
|
1015
|
+
const editorStore = useEditorStore();
|
|
1016
|
+
const applicationStore = useApplicationStore();
|
|
1017
|
+
const dependencyEditorState =
|
|
1018
|
+
editorStore.projectConfigurationEditorState.projectDependencyEditorState;
|
|
1019
|
+
const [selectedTransitiveDependency, setSelectedTransitiveDependency] =
|
|
1020
|
+
useState<TransitiveDependencyOption | null>(null);
|
|
1021
|
+
const [transitiveDependencyOptions, setTransitiveDependencyOptions] =
|
|
1022
|
+
useState<TransitiveDependencyOption[]>([]);
|
|
1023
|
+
|
|
1024
|
+
const getTransitiveDependencies =
|
|
1025
|
+
useCallback((): TransitiveDependencyOption[] => {
|
|
1026
|
+
const dependencyReport = dependencyEditorState.dependencyReport;
|
|
1027
|
+
if (!dependencyReport?.graph) {
|
|
1028
|
+
return [];
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const transitiveDeps: { [key: string]: TransitiveDependencyOption } =
|
|
1032
|
+
{};
|
|
1033
|
+
const existingExclusionCoordinates =
|
|
1034
|
+
dependencyEditorState.getExclusionCoordinates(
|
|
1035
|
+
projectDependency.projectId,
|
|
1036
|
+
);
|
|
1037
|
+
|
|
1038
|
+
const visitedNodes: { [key: string]: boolean } = {};
|
|
1039
|
+
const traverseNode = (nodeId: string) => {
|
|
1040
|
+
if (visitedNodes[nodeId]) {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
visitedNodes[nodeId] = true;
|
|
1044
|
+
|
|
1045
|
+
const node = dependencyReport.graph.nodes.get(nodeId);
|
|
1046
|
+
if (node?.dependencies) {
|
|
1047
|
+
for (let i = 0; i < node.dependencies.length; i++) {
|
|
1048
|
+
const dep = node.dependencies[i];
|
|
1049
|
+
if (!dep?.groupId || !dep.artifactId) {
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
const coordinate = generateGAVCoordinates(
|
|
1053
|
+
dep.groupId,
|
|
1054
|
+
dep.artifactId,
|
|
1055
|
+
undefined,
|
|
1056
|
+
);
|
|
1057
|
+
|
|
1058
|
+
if (
|
|
1059
|
+
existingExclusionCoordinates.indexOf(coordinate) === -1 &&
|
|
1060
|
+
coordinate !==
|
|
1061
|
+
`${projectDependency.groupId}:${projectDependency.artifactId}`
|
|
1062
|
+
) {
|
|
1063
|
+
transitiveDeps[coordinate] = {
|
|
1064
|
+
label: generateGAVCoordinates(
|
|
1065
|
+
dep.groupId,
|
|
1066
|
+
dep.artifactId,
|
|
1067
|
+
undefined,
|
|
1068
|
+
),
|
|
1069
|
+
value: coordinate,
|
|
1070
|
+
groupId: dep.groupId,
|
|
1071
|
+
artifactId: dep.artifactId,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
traverseNode(dep.id);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
const rootNodeId = generateGAVCoordinates(
|
|
1081
|
+
guaranteeNonNullable(projectDependency.groupId),
|
|
1082
|
+
guaranteeNonNullable(projectDependency.artifactId),
|
|
1083
|
+
guaranteeNonNullable(projectDependency.versionId),
|
|
1084
|
+
);
|
|
1085
|
+
traverseNode(rootNodeId);
|
|
1086
|
+
|
|
1087
|
+
const transitiveDepsArray: TransitiveDependencyOption[] = [];
|
|
1088
|
+
for (const coordinate in transitiveDeps) {
|
|
1089
|
+
if (
|
|
1090
|
+
Object.prototype.hasOwnProperty.call(transitiveDeps, coordinate)
|
|
1091
|
+
) {
|
|
1092
|
+
if (!transitiveDeps[coordinate]) {
|
|
1093
|
+
continue;
|
|
1094
|
+
} else {
|
|
1095
|
+
transitiveDepsArray.push(transitiveDeps[coordinate]);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
return transitiveDepsArray.sort((a, b) =>
|
|
1101
|
+
a.label.localeCompare(b.label),
|
|
1102
|
+
);
|
|
1103
|
+
}, [
|
|
1104
|
+
dependencyEditorState,
|
|
1105
|
+
projectDependency.projectId,
|
|
1106
|
+
projectDependency.groupId,
|
|
1107
|
+
projectDependency.artifactId,
|
|
1108
|
+
projectDependency.versionId,
|
|
1109
|
+
]);
|
|
1110
|
+
|
|
1111
|
+
useEffect(() => {
|
|
1112
|
+
setTransitiveDependencyOptions(getTransitiveDependencies());
|
|
1113
|
+
}, [
|
|
1114
|
+
dependencyEditorState.dependencyReport,
|
|
1115
|
+
projectDependency.projectId,
|
|
1116
|
+
getTransitiveDependencies,
|
|
1117
|
+
]);
|
|
1118
|
+
|
|
1119
|
+
const addExclusionFromDropdown = (
|
|
1120
|
+
option: TransitiveDependencyOption | null,
|
|
1121
|
+
): void => {
|
|
1122
|
+
if (!option) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
dependencyEditorState.addExclusionByCoordinate(
|
|
1128
|
+
projectDependency.projectId,
|
|
1129
|
+
option.value,
|
|
1130
|
+
);
|
|
1131
|
+
setSelectedTransitiveDependency(null);
|
|
1132
|
+
setTransitiveDependencyOptions(getTransitiveDependencies());
|
|
1133
|
+
flowResult(dependencyEditorState.fetchDependencyReport())
|
|
1134
|
+
.then(() => {
|
|
1135
|
+
setTransitiveDependencyOptions(getTransitiveDependencies());
|
|
1136
|
+
})
|
|
1137
|
+
.catch(applicationStore.alertUnhandledError);
|
|
1138
|
+
|
|
1139
|
+
applicationStore.notificationService.notifySuccess(
|
|
1140
|
+
`Exclusion added: ${option.value}`,
|
|
1141
|
+
);
|
|
1142
|
+
} catch (error) {
|
|
1143
|
+
assertErrorThrown(error);
|
|
1144
|
+
applicationStore.notificationService.notifyError(
|
|
1145
|
+
`Failed to add exclusion: ${error.message}`,
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
|
|
1150
|
+
if (isReadOnly) {
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return (
|
|
1155
|
+
<div className="project-dependency-exclusions-selector">
|
|
1156
|
+
<CustomSelectorInput
|
|
1157
|
+
className="project-dependency-exclusions-selector__dropdown"
|
|
1158
|
+
placeholder="Add exclusion..."
|
|
1159
|
+
options={transitiveDependencyOptions}
|
|
1160
|
+
onChange={addExclusionFromDropdown}
|
|
1161
|
+
value={selectedTransitiveDependency}
|
|
1162
|
+
isClearable={true}
|
|
1163
|
+
escapeClearsValue={true}
|
|
1164
|
+
disabled={transitiveDependencyOptions.length === 0}
|
|
1165
|
+
darkMode={
|
|
1166
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
|
1167
|
+
}
|
|
1168
|
+
/>
|
|
1169
|
+
</div>
|
|
1170
|
+
);
|
|
1171
|
+
},
|
|
1172
|
+
);
|
|
1173
|
+
|
|
1174
|
+
const ProjectDependencyExclusionsList = observer(
|
|
1175
|
+
(props: { projectDependency: ProjectDependency; isReadOnly: boolean }) => {
|
|
1176
|
+
const { projectDependency, isReadOnly } = props;
|
|
1177
|
+
const editorStore = useEditorStore();
|
|
1178
|
+
const applicationStore = useApplicationStore();
|
|
1179
|
+
const dependencyEditorState =
|
|
1180
|
+
editorStore.projectConfigurationEditorState.projectDependencyEditorState;
|
|
1181
|
+
const [, setForceUpdate] = useState(0);
|
|
1182
|
+
const exclusions = dependencyEditorState.getExclusions(
|
|
1183
|
+
projectDependency.projectId,
|
|
1184
|
+
);
|
|
1185
|
+
|
|
1186
|
+
useEffect(() => {
|
|
1187
|
+
setForceUpdate((prev) => prev + 1);
|
|
1188
|
+
}, [
|
|
1189
|
+
dependencyEditorState.dependencyReport,
|
|
1190
|
+
projectDependency.projectId,
|
|
1191
|
+
dependencyEditorState,
|
|
1192
|
+
]);
|
|
1193
|
+
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
const interval = setInterval(() => {
|
|
1196
|
+
const currentExclusions = dependencyEditorState.getExclusions(
|
|
1197
|
+
projectDependency.projectId,
|
|
1198
|
+
);
|
|
1199
|
+
if (currentExclusions.length !== exclusions.length) {
|
|
1200
|
+
setForceUpdate((prev) => prev + 1);
|
|
1201
|
+
}
|
|
1202
|
+
}, 1000);
|
|
1203
|
+
|
|
1204
|
+
return () => clearInterval(interval);
|
|
1205
|
+
}, [exclusions.length, dependencyEditorState, projectDependency.projectId]);
|
|
1206
|
+
|
|
1207
|
+
const removeExclusion = (exclusion: ProjectDependencyExclusion): void => {
|
|
1208
|
+
try {
|
|
1209
|
+
dependencyEditorState.removeExclusion(
|
|
1210
|
+
projectDependency.projectId,
|
|
1211
|
+
exclusion,
|
|
1212
|
+
);
|
|
1213
|
+
|
|
1214
|
+
flowResult(dependencyEditorState.fetchDependencyReport()).catch(
|
|
1215
|
+
applicationStore.alertUnhandledError,
|
|
1216
|
+
);
|
|
1217
|
+
|
|
1218
|
+
applicationStore.notificationService.notifySuccess(
|
|
1219
|
+
`Exclusion removed: ${exclusion.coordinate}`,
|
|
1220
|
+
);
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
assertErrorThrown(error);
|
|
1223
|
+
applicationStore.notificationService.notifyError(
|
|
1224
|
+
`Failed to remove exclusion: ${error.message}`,
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
if (exclusions.length === 0) {
|
|
1230
|
+
return null;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return (
|
|
1234
|
+
<div className="project-dependency-exclusions-list">
|
|
1235
|
+
<div className="project-dependency-exclusions-list__header">
|
|
1236
|
+
<div className="project-dependency-exclusions-list__title">
|
|
1237
|
+
Exclusions ({exclusions.length})
|
|
1238
|
+
</div>
|
|
1239
|
+
</div>
|
|
1240
|
+
<div className="project-dependency-exclusions-list__items">
|
|
1241
|
+
{exclusions.map((exclusion) => (
|
|
1242
|
+
<div
|
|
1243
|
+
key={exclusion.coordinate}
|
|
1244
|
+
className="project-dependency-exclusions-list__item"
|
|
1245
|
+
>
|
|
1246
|
+
<div className="project-dependency-exclusions-list__item__coordinate">
|
|
1247
|
+
{exclusion.coordinate}
|
|
1248
|
+
</div>
|
|
1249
|
+
{!isReadOnly && (
|
|
1250
|
+
<button
|
|
1251
|
+
className="project-dependency-exclusions-list__item__remove-btn btn--dark btn--caution"
|
|
1252
|
+
onClick={() => removeExclusion(exclusion)}
|
|
1253
|
+
title="Remove exclusion"
|
|
1254
|
+
>
|
|
1255
|
+
<TimesIcon />
|
|
1256
|
+
</button>
|
|
1257
|
+
)}
|
|
1258
|
+
</div>
|
|
1259
|
+
))}
|
|
1260
|
+
</div>
|
|
1261
|
+
</div>
|
|
1262
|
+
);
|
|
1263
|
+
},
|
|
1264
|
+
);
|
|
1265
|
+
|
|
750
1266
|
const ProjectVersionDependencyEditor = observer(
|
|
751
1267
|
(props: {
|
|
752
1268
|
projectDependency: ProjectDependency;
|
|
@@ -754,7 +1270,6 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
754
1270
|
isReadOnly: boolean;
|
|
755
1271
|
projects: Map<string, StoreProjectData>;
|
|
756
1272
|
}) => {
|
|
757
|
-
// init
|
|
758
1273
|
const { projectDependency, deleteValue, isReadOnly, projects } = props;
|
|
759
1274
|
const projectDependencyData = projects.get(projectDependency.projectId);
|
|
760
1275
|
const editorStore = useEditorStore();
|
|
@@ -776,12 +1291,14 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
776
1291
|
const projectDisabled =
|
|
777
1292
|
!configState.associatedProjectsAndVersionsFetched ||
|
|
778
1293
|
configState.isReadOnly;
|
|
779
|
-
const
|
|
1294
|
+
const projectsArray: StoreProjectData[] = [];
|
|
1295
|
+
configState.projects.forEach((project: StoreProjectData) => {
|
|
1296
|
+
projectsArray.push(project);
|
|
1297
|
+
});
|
|
1298
|
+
const projectsOptions = projectsArray
|
|
780
1299
|
.map(buildProjectOption)
|
|
781
1300
|
.sort(compareLabelFn);
|
|
782
|
-
const onProjectSelectionChange =
|
|
783
|
-
val: ProjectOption | null,
|
|
784
|
-
): Promise<void> => {
|
|
1301
|
+
const onProjectSelectionChange = (val: ProjectOption | null): void => {
|
|
785
1302
|
if (
|
|
786
1303
|
(val !== null || selectedProjectOption !== null) &&
|
|
787
1304
|
(!val ||
|
|
@@ -793,26 +1310,36 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
793
1310
|
if (val) {
|
|
794
1311
|
try {
|
|
795
1312
|
fetchSelectedProjectVersionsStatus.inProgress();
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1313
|
+
editorStore.depotServerClient
|
|
1314
|
+
.getVersions(
|
|
1315
|
+
guaranteeNonNullable(projectDependency.groupId),
|
|
1316
|
+
guaranteeNonNullable(projectDependency.artifactId),
|
|
1317
|
+
true,
|
|
1318
|
+
)
|
|
1319
|
+
.then((_versions) => {
|
|
1320
|
+
configState.versions.set(val.value.coordinates, _versions);
|
|
1321
|
+
if (_versions.length) {
|
|
1322
|
+
projectDependency.setVersionId(
|
|
1323
|
+
guaranteeNonNullable(_versions[_versions.length - 1]),
|
|
1324
|
+
);
|
|
1325
|
+
flowResult(
|
|
1326
|
+
dependencyEditorState.fetchDependencyReport(),
|
|
1327
|
+
).catch(applicationStore.alertUnhandledError);
|
|
1328
|
+
} else {
|
|
1329
|
+
projectDependency.setVersionId('');
|
|
1330
|
+
}
|
|
1331
|
+
fetchSelectedProjectVersionsStatus.reset();
|
|
1332
|
+
})
|
|
1333
|
+
.catch((error) => {
|
|
1334
|
+
assertErrorThrown(error);
|
|
1335
|
+
editorStore.applicationStore.notificationService.notifyError(
|
|
1336
|
+
error,
|
|
1337
|
+
);
|
|
1338
|
+
fetchSelectedProjectVersionsStatus.reset();
|
|
1339
|
+
});
|
|
812
1340
|
} catch (error) {
|
|
813
1341
|
assertErrorThrown(error);
|
|
814
1342
|
editorStore.applicationStore.notificationService.notifyError(error);
|
|
815
|
-
} finally {
|
|
816
1343
|
fetchSelectedProjectVersionsStatus.reset();
|
|
817
1344
|
}
|
|
818
1345
|
}
|
|
@@ -820,19 +1347,26 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
820
1347
|
};
|
|
821
1348
|
// version
|
|
822
1349
|
const version = projectDependency.versionId;
|
|
823
|
-
const
|
|
824
|
-
.
|
|
825
|
-
.
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
|
|
1350
|
+
const sortedVersions = versions
|
|
1351
|
+
.slice()
|
|
1352
|
+
.sort((v1, v2) => compareSemVerVersions(v2, v1));
|
|
1353
|
+
const versionOptions = sortedVersions.map((v) => {
|
|
1354
|
+
if (v === MASTER_SNAPSHOT_ALIAS) {
|
|
1355
|
+
return { value: v, label: SNAPSHOT_VERSION_ALIAS };
|
|
1356
|
+
}
|
|
1357
|
+
return { value: v, label: v };
|
|
1358
|
+
});
|
|
1359
|
+
let selectedVersionOption: VersionOption | null = null;
|
|
1360
|
+
for (let i = 0; i < versionOptions.length; i++) {
|
|
1361
|
+
if (guaranteeNonNullable(versionOptions[i]).value === version) {
|
|
1362
|
+
selectedVersionOption = guaranteeNonNullable(versionOptions[i]);
|
|
1363
|
+
break;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
833
1366
|
const versionDisabled =
|
|
834
|
-
|
|
835
|
-
!
|
|
1367
|
+
!guaranteeNonNullable(versions.length) ||
|
|
1368
|
+
!guaranteeNonNullable(projectDependency.projectId.length) ||
|
|
1369
|
+
!guaranteeNonNullable(configState.associatedProjectsAndVersionsFetched) ||
|
|
836
1370
|
isReadOnly;
|
|
837
1371
|
|
|
838
1372
|
const onVersionSelectionChange = (val: VersionOption | null): void => {
|
|
@@ -901,9 +1435,7 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
901
1435
|
isClearable={true}
|
|
902
1436
|
escapeClearsValue={true}
|
|
903
1437
|
onChange={(val: ProjectOption | null) => {
|
|
904
|
-
onProjectSelectionChange(val)
|
|
905
|
-
applicationStore.alertUnhandledError,
|
|
906
|
-
);
|
|
1438
|
+
onProjectSelectionChange(val);
|
|
907
1439
|
}}
|
|
908
1440
|
value={selectedProjectOption}
|
|
909
1441
|
isLoading={configState.fetchingProjectVersionsState.isInProgress}
|
|
@@ -935,6 +1467,13 @@ const ProjectVersionDependencyEditor = observer(
|
|
|
935
1467
|
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
|
936
1468
|
}
|
|
937
1469
|
/>
|
|
1470
|
+
{selectedProject && selectedVersionOption && (
|
|
1471
|
+
<ProjectDependencyInlineExclusionsSelector
|
|
1472
|
+
projectDependency={projectDependency}
|
|
1473
|
+
isReadOnly={isReadOnly}
|
|
1474
|
+
/>
|
|
1475
|
+
)}
|
|
1476
|
+
|
|
938
1477
|
<ControlledDropdownMenu
|
|
939
1478
|
className="project-dependency-editor__visit-project-btn__dropdown-trigger btn--medium"
|
|
940
1479
|
content={
|
|
@@ -1013,13 +1552,20 @@ export const ProjectDependencyEditor = observer(() => {
|
|
|
1013
1552
|
<ProjectDependencyActions dependencyEditorState={dependencyEditorState} />
|
|
1014
1553
|
{currentProjectConfiguration.projectDependencies.map(
|
|
1015
1554
|
(projectDependency) => (
|
|
1016
|
-
<
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1555
|
+
<div key={projectDependency._UUID}>
|
|
1556
|
+
<ProjectVersionDependencyEditor
|
|
1557
|
+
projectDependency={projectDependency}
|
|
1558
|
+
deleteValue={deleteProjectDependency(projectDependency)}
|
|
1559
|
+
isReadOnly={isReadOnly}
|
|
1560
|
+
projects={configState.projects}
|
|
1561
|
+
/>
|
|
1562
|
+
{/* Indented exclusions list */}
|
|
1563
|
+
<ProjectDependencyExclusionsList
|
|
1564
|
+
key={`${projectDependency.projectId}-exclusions`}
|
|
1565
|
+
projectDependency={projectDependency}
|
|
1566
|
+
isReadOnly={isReadOnly}
|
|
1567
|
+
/>
|
|
1568
|
+
</div>
|
|
1023
1569
|
),
|
|
1024
1570
|
)}
|
|
1025
1571
|
{dependencyEditorState.reportTab && (
|