@finos/legend-application-studio 22.2.0 → 22.2.1

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.
Files changed (34) hide show
  1. package/lib/index.css +1 -1
  2. package/lib/package.json +1 -1
  3. package/lib/stores/EditorGraphState.d.ts.map +1 -1
  4. package/lib/stores/EditorGraphState.js +10 -46
  5. package/lib/stores/EditorGraphState.js.map +1 -1
  6. package/lib/stores/EditorStore.d.ts +1 -1
  7. package/lib/stores/EditorStore.d.ts.map +1 -1
  8. package/lib/stores/EditorStore.js +8 -6
  9. package/lib/stores/EditorStore.js.map +1 -1
  10. package/lib/stores/EditorTabManagerState.d.ts +1 -2
  11. package/lib/stores/EditorTabManagerState.d.ts.map +1 -1
  12. package/lib/stores/EditorTabManagerState.js +2 -14
  13. package/lib/stores/EditorTabManagerState.js.map +1 -1
  14. package/lib/stores/ExplorerTreeState.d.ts.map +1 -1
  15. package/lib/stores/ExplorerTreeState.js +1 -3
  16. package/lib/stores/ExplorerTreeState.js.map +1 -1
  17. package/lib/stores/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.d.ts.map +1 -1
  18. package/lib/stores/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.js +1 -0
  19. package/lib/stores/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.js.map +1 -1
  20. package/lib/stores/project-viewer/ProjectViewerStore.d.ts.map +1 -1
  21. package/lib/stores/project-viewer/ProjectViewerStore.js +1 -1
  22. package/lib/stores/project-viewer/ProjectViewerStore.js.map +1 -1
  23. package/lib/stores/sidebar-state/LocalChangesState.d.ts +16 -3
  24. package/lib/stores/sidebar-state/LocalChangesState.d.ts.map +1 -1
  25. package/lib/stores/sidebar-state/LocalChangesState.js +143 -246
  26. package/lib/stores/sidebar-state/LocalChangesState.js.map +1 -1
  27. package/package.json +10 -10
  28. package/src/stores/EditorGraphState.ts +18 -49
  29. package/src/stores/EditorStore.ts +12 -10
  30. package/src/stores/EditorTabManagerState.ts +1 -20
  31. package/src/stores/ExplorerTreeState.ts +4 -6
  32. package/src/stores/editor-state/project-configuration-editor-state/ProjectDependencyEditorState.ts +2 -0
  33. package/src/stores/project-viewer/ProjectViewerStore.ts +3 -1
  34. package/src/stores/sidebar-state/LocalChangesState.ts +279 -450
@@ -189,6 +189,8 @@ export abstract class LocalChangesState {
189
189
  refreshWorkspaceSyncStatus: flow,
190
190
  refreshLocalChanges: flow,
191
191
  pushLocalChanges: flow,
192
+ processConflicts: flow,
193
+ restartChangeDetection: flow,
192
194
  });
193
195
 
194
196
  this.editorStore = editorStore;
@@ -306,198 +308,15 @@ export abstract class LocalChangesState {
306
308
 
307
309
  abstract refreshLocalChanges(): GeneratorFn<void>;
308
310
 
309
- abstract pushLocalChanges(pushMessage?: string): GeneratorFn<void>;
310
- }
311
+ abstract processConflicts(): GeneratorFn<void>;
311
312
 
312
- export class FormLocalChangesState extends LocalChangesState {
313
- constructor(editorStore: EditorStore, sdlcState: EditorSDLCState) {
314
- super(editorStore, sdlcState);
315
- makeObservable(this, {
316
- openPotentialWorkspacePullConflict: action,
317
- });
318
- }
319
- openLocalChange(diff: EntityDiff): void {
320
- const fromEntityGetter = (
321
- entityPath: string | undefined,
322
- ): Entity | undefined => {
323
- if (entityPath) {
324
- return this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
325
- (e) => e.path === entityPath,
326
- );
327
- }
328
- return undefined;
329
- };
330
- const toEntityGetter = (
331
- entityPath: string | undefined,
332
- ): Entity | undefined => {
333
- if (!entityPath) {
334
- return undefined;
335
- }
336
- const element =
337
- this.editorStore.graphManagerState.graph.getNullableElement(entityPath);
338
- if (!element) {
339
- return undefined;
340
- }
341
- const entity =
342
- this.editorStore.graphManagerState.graphManager.elementToEntity(
343
- element,
344
- {
345
- pruneSourceInformation: true,
346
- },
347
- );
348
- return entity;
349
- };
350
- const fromEntity = EntityDiff.shouldOldEntityExist(diff)
351
- ? guaranteeNonNullable(
352
- fromEntityGetter(diff.getValidatedOldPath()),
353
- `Can't find entity with path '${diff.oldPath}'`,
354
- )
355
- : undefined;
356
- const toEntity = EntityDiff.shouldNewEntityExist(diff)
357
- ? guaranteeNonNullable(
358
- toEntityGetter(diff.getValidatedNewPath()),
359
- `Can't find entity with path '${diff.newPath}'`,
360
- )
361
- : undefined;
362
- this.editorStore.tabManagerState.openTab(
363
- new EntityDiffViewState(
364
- this.editorStore,
365
- SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
366
- SPECIAL_REVISION_ALIAS.LOCAL,
367
- diff.oldPath,
368
- diff.newPath,
369
- fromEntity,
370
- toEntity,
371
- fromEntityGetter,
372
- toEntityGetter,
373
- ),
374
- );
375
- }
313
+ abstract getLocalChanges(): EntityChange[];
376
314
 
377
- openWorkspacePullChange(diff: EntityDiff): void {
378
- const fromEntityGetter = (
379
- entityPath: string | undefined,
380
- ): Entity | undefined => {
381
- if (entityPath) {
382
- return this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
383
- (e) => e.path === entityPath,
384
- );
385
- }
386
- return undefined;
387
- };
388
- const toEntityGetter = (
389
- entityPath: string | undefined,
390
- ): Entity | undefined => {
391
- if (entityPath) {
392
- return this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.entities.find(
393
- (e) => e.path === entityPath,
394
- );
395
- }
396
- return undefined;
397
- };
398
- const fromEntity = EntityDiff.shouldOldEntityExist(diff)
399
- ? guaranteeNonNullable(
400
- fromEntityGetter(diff.getValidatedOldPath()),
401
- `Can't find entity with path '${diff.oldPath}'`,
402
- )
403
- : undefined;
404
- const toEntity = EntityDiff.shouldNewEntityExist(diff)
405
- ? guaranteeNonNullable(
406
- toEntityGetter(diff.getValidatedNewPath()),
407
- `Can't find entity with path '${diff.newPath}'`,
408
- )
409
- : undefined;
410
- this.editorStore.tabManagerState.openTab(
411
- new EntityDiffViewState(
412
- this.editorStore,
413
- SPECIAL_REVISION_ALIAS.LOCAL,
414
- SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
415
- diff.oldPath,
416
- diff.newPath,
417
- fromEntity,
418
- toEntity,
419
- fromEntityGetter,
420
- toEntityGetter,
421
- ),
422
- );
423
- }
315
+ abstract getCurrentHashIndexes(): Map<string, string>;
424
316
 
425
- openPotentialWorkspacePullConflict(conflict: EntityChangeConflict): void {
426
- const baseEntityGetter = (
427
- entityPath: string | undefined,
428
- ): Entity | undefined =>
429
- entityPath
430
- ? this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
431
- (e) => e.path === entityPath,
432
- )
433
- : undefined;
434
- const currentChangeEntityGetter = (
435
- entityPath: string | undefined,
436
- ): Entity | undefined =>
437
- entityPath
438
- ? this.editorStore.graphManagerState.graph.allOwnElements
439
- .map((element) =>
440
- this.editorStore.graphManagerState.graphManager.elementToEntity(
441
- element,
442
- ),
443
- )
444
- .find((e) => e.path === entityPath)
445
- : undefined;
446
- const incomingChangeEntityGetter = (
447
- entityPath: string | undefined,
448
- ): Entity | undefined =>
449
- entityPath
450
- ? this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.entities.find(
451
- (e) => e.path === entityPath,
452
- )
453
- : undefined;
454
- const conflictEditorState = new EntityChangeConflictEditorState(
455
- this.editorStore,
456
- this.editorStore.conflictResolutionState,
457
- conflict.entityPath,
458
- SPECIAL_REVISION_ALIAS.WORKSPACE_BASE,
459
- SPECIAL_REVISION_ALIAS.LOCAL,
460
- SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
461
- baseEntityGetter(conflict.entityPath),
462
- currentChangeEntityGetter(conflict.entityPath),
463
- incomingChangeEntityGetter(conflict.entityPath),
464
- baseEntityGetter,
465
- currentChangeEntityGetter,
466
- incomingChangeEntityGetter,
467
- );
468
- conflictEditorState.setReadOnly(true);
469
- this.editorStore.tabManagerState.openTab(conflictEditorState);
470
- }
317
+ abstract stopChangeDetection(): void;
471
318
 
472
- *refreshLocalChanges(): GeneratorFn<void> {
473
- const startTime = Date.now();
474
- this.refreshLocalChangesDetectorState.inProgress();
475
- try {
476
- // ======= (RE)START CHANGE DETECTION =======
477
- this.editorStore.changeDetectionState.stop();
478
- yield Promise.all([
479
- this.sdlcState.buildWorkspaceLatestRevisionEntityHashesIndex(),
480
- this.editorStore.changeDetectionState.preComputeGraphElementHashes(),
481
- ]);
482
- this.editorStore.changeDetectionState.start();
483
- this.editorStore.applicationStore.log.info(
484
- LogEvent.create(CHANGE_DETECTION_EVENT.CHANGE_DETECTION_RESTARTED),
485
- Date.now() - startTime,
486
- 'ms',
487
- );
488
- // ======= FINISHED (RE)START CHANGE DETECTION =======
489
- } catch (error) {
490
- assertErrorThrown(error);
491
- this.editorStore.applicationStore.log.error(
492
- LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
493
- error,
494
- );
495
- this.editorStore.applicationStore.notifyError(error);
496
- this.sdlcState.handleChangeDetectionRefreshIssue(error);
497
- } finally {
498
- this.refreshLocalChangesDetectorState.complete();
499
- }
500
- }
319
+ abstract restartChangeDetection(): GeneratorFn<void>;
501
320
 
502
321
  *pushLocalChanges(pushMessage?: string): GeneratorFn<void> {
503
322
  if (
@@ -507,29 +326,11 @@ export class FormLocalChangesState extends LocalChangesState {
507
326
  return;
508
327
  }
509
328
  // check if the workspace is in conflict resolution mode
510
- try {
511
- const isInConflictResolutionMode = (yield flowResult(
512
- this.sdlcState.checkIfCurrentWorkspaceIsInConflictResolutionMode(),
513
- )) as boolean;
514
- if (isInConflictResolutionMode) {
515
- this.editorStore.applicationStore.setBlockingAlert({
516
- message: 'Workspace is in conflict resolution mode',
517
- prompt: 'Please refresh the application',
518
- });
519
- return;
520
- }
521
- } catch (error) {
522
- assertErrorThrown(error);
523
- this.editorStore.applicationStore.notifyWarning(
524
- 'Failed to check if current workspace is in conflict resolution mode',
525
- );
526
- return;
527
- }
329
+ yield flowResult(this.processConflicts());
528
330
 
529
331
  this.pushChangesState.inProgress();
530
332
  const startTime = Date.now();
531
- const localChanges =
532
- this.editorStore.graphState.computeLocalEntityChanges();
333
+ const localChanges = this.getLocalChanges();
533
334
  if (!localChanges.length) {
534
335
  this.pushChangesState.complete();
535
336
  return;
@@ -587,8 +388,7 @@ export class FormLocalChangesState extends LocalChangesState {
587
388
  this.pushChangesState.complete();
588
389
  return;
589
390
  }
590
- const currentHashesIndex =
591
- this.editorStore.changeDetectionState.snapshotLocalEntityHashesIndex();
391
+ const currentHashesIndex = this.getCurrentHashIndexes();
592
392
 
593
393
  try {
594
394
  const nullableRevisionChange =
@@ -626,7 +426,7 @@ export class FormLocalChangesState extends LocalChangesState {
626
426
 
627
427
  // ======= (RE)START CHANGE DETECTION =======
628
428
 
629
- this.editorStore.changeDetectionState.stop();
429
+ this.stopChangeDetection();
630
430
 
631
431
  try {
632
432
  /**
@@ -700,14 +500,7 @@ export class FormLocalChangesState extends LocalChangesState {
700
500
  throw error;
701
501
  }
702
502
  }
703
- yield this.editorStore.changeDetectionState.preComputeGraphElementHashes();
704
- this.editorStore.changeDetectionState.start();
705
- yield Promise.all([
706
- this.editorStore.changeDetectionState.computeAggregatedWorkspaceChanges(
707
- true,
708
- ),
709
- ]);
710
-
503
+ yield flowResult(this.restartChangeDetection());
711
504
  this.editorStore.applicationStore.log.info(
712
505
  LogEvent.create(CHANGE_DETECTION_EVENT.CHANGE_DETECTION_RESTARTED),
713
506
  Date.now() - syncFinishedTime,
@@ -739,263 +532,299 @@ export class FormLocalChangesState extends LocalChangesState {
739
532
  }
740
533
  }
741
534
 
742
- export class TextLocalChangesState extends LocalChangesState {
743
- localChanges: EntityChange[] = [];
744
-
535
+ export class FormLocalChangesState extends LocalChangesState {
745
536
  constructor(editorStore: EditorStore, sdlcState: EditorSDLCState) {
746
537
  super(editorStore, sdlcState);
747
538
  makeObservable(this, {
748
- setLocalChanges: action,
539
+ openPotentialWorkspacePullConflict: action,
749
540
  });
750
541
  }
542
+ openLocalChange(diff: EntityDiff): void {
543
+ const fromEntityGetter = (
544
+ entityPath: string | undefined,
545
+ ): Entity | undefined => {
546
+ if (entityPath) {
547
+ return this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
548
+ (e) => e.path === entityPath,
549
+ );
550
+ }
551
+ return undefined;
552
+ };
553
+ const toEntityGetter = (
554
+ entityPath: string | undefined,
555
+ ): Entity | undefined => {
556
+ if (!entityPath) {
557
+ return undefined;
558
+ }
559
+ const element =
560
+ this.editorStore.graphManagerState.graph.getNullableElement(entityPath);
561
+ if (!element) {
562
+ return undefined;
563
+ }
564
+ const entity =
565
+ this.editorStore.graphManagerState.graphManager.elementToEntity(
566
+ element,
567
+ {
568
+ pruneSourceInformation: true,
569
+ },
570
+ );
571
+ return entity;
572
+ };
573
+ const fromEntity = EntityDiff.shouldOldEntityExist(diff)
574
+ ? guaranteeNonNullable(
575
+ fromEntityGetter(diff.getValidatedOldPath()),
576
+ `Can't find entity with path '${diff.oldPath}'`,
577
+ )
578
+ : undefined;
579
+ const toEntity = EntityDiff.shouldNewEntityExist(diff)
580
+ ? guaranteeNonNullable(
581
+ toEntityGetter(diff.getValidatedNewPath()),
582
+ `Can't find entity with path '${diff.newPath}'`,
583
+ )
584
+ : undefined;
585
+ this.editorStore.tabManagerState.openTab(
586
+ new EntityDiffViewState(
587
+ this.editorStore,
588
+ SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
589
+ SPECIAL_REVISION_ALIAS.LOCAL,
590
+ diff.oldPath,
591
+ diff.newPath,
592
+ fromEntity,
593
+ toEntity,
594
+ fromEntityGetter,
595
+ toEntityGetter,
596
+ ),
597
+ );
598
+ }
751
599
 
752
- setLocalChanges(val: EntityChange[]): void {
753
- this.localChanges = val;
754
- }
755
-
756
- *refreshLocalChanges(): GeneratorFn<void> {
757
- const startTime = Date.now();
758
- this.refreshLocalChangesDetectorState.inProgress();
759
- try {
760
- // ======= (RE)START CHANGE DETECTION =======
761
- yield Promise.all([
762
- this.sdlcState.buildWorkspaceLatestRevisionEntityHashesIndex(),
763
- this.editorStore.changeDetectionState.preComputeGraphElementHashes(),
764
- ]);
765
- this.editorStore.changeDetectionState.computeLocalChangesInTextMode(
766
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState
767
- .entities,
768
- );
769
- this.editorStore.applicationStore.log.info(
770
- LogEvent.create(CHANGE_DETECTION_EVENT.CHANGE_DETECTION_RESTARTED),
771
- Date.now() - startTime,
772
- 'ms',
773
- );
774
- // ======= FINISHED (RE)START CHANGE DETECTION =======
775
- } catch (error) {
776
- assertErrorThrown(error);
777
- this.editorStore.applicationStore.log.error(
778
- LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
779
- error,
780
- );
781
- this.editorStore.applicationStore.notifyError(error);
782
- this.sdlcState.handleChangeDetectionRefreshIssue(error);
783
- } finally {
784
- this.refreshLocalChangesDetectorState.complete();
785
- }
786
- }
787
-
788
- *pushLocalChanges(pushMessage?: string): GeneratorFn<void> {
789
- if (
790
- this.pushChangesState.isInProgress ||
791
- this.editorStore.workspaceUpdaterState.isUpdatingWorkspace
792
- ) {
793
- return;
794
- }
795
-
796
- this.pushChangesState.inProgress();
797
- const startTime = Date.now();
798
- if (!this.localChanges.length) {
799
- this.pushChangesState.complete();
800
- return;
801
- }
802
- yield flowResult(
803
- this.sdlcState.fetchRemoteWorkspaceRevision(
804
- this.sdlcState.activeProject.projectId,
805
- this.sdlcState.activeWorkspace,
600
+ openWorkspacePullChange(diff: EntityDiff): void {
601
+ const fromEntityGetter = (
602
+ entityPath: string | undefined,
603
+ ): Entity | undefined => {
604
+ if (entityPath) {
605
+ return this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
606
+ (e) => e.path === entityPath,
607
+ );
608
+ }
609
+ return undefined;
610
+ };
611
+ const toEntityGetter = (
612
+ entityPath: string | undefined,
613
+ ): Entity | undefined => {
614
+ if (entityPath) {
615
+ return this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.entities.find(
616
+ (e) => e.path === entityPath,
617
+ );
618
+ }
619
+ return undefined;
620
+ };
621
+ const fromEntity = EntityDiff.shouldOldEntityExist(diff)
622
+ ? guaranteeNonNullable(
623
+ fromEntityGetter(diff.getValidatedOldPath()),
624
+ `Can't find entity with path '${diff.oldPath}'`,
625
+ )
626
+ : undefined;
627
+ const toEntity = EntityDiff.shouldNewEntityExist(diff)
628
+ ? guaranteeNonNullable(
629
+ toEntityGetter(diff.getValidatedNewPath()),
630
+ `Can't find entity with path '${diff.newPath}'`,
631
+ )
632
+ : undefined;
633
+ this.editorStore.tabManagerState.openTab(
634
+ new EntityDiffViewState(
635
+ this.editorStore,
636
+ SPECIAL_REVISION_ALIAS.LOCAL,
637
+ SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
638
+ diff.oldPath,
639
+ diff.newPath,
640
+ fromEntity,
641
+ toEntity,
642
+ fromEntityGetter,
643
+ toEntityGetter,
806
644
  ),
807
645
  );
808
- if (this.sdlcState.isWorkspaceOutOfSync) {
809
- // ensure changes have been computed for latest remote version
810
- const remoteWorkspaceEntities =
811
- (yield this.editorStore.sdlcServerClient.getEntitiesByRevision(
812
- this.sdlcState.activeProject.projectId,
813
- this.sdlcState.activeWorkspace,
814
- this.sdlcState.activeRemoteWorkspaceRevision.id,
815
- )) as Entity[];
816
- this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.setEntities(
817
- remoteWorkspaceEntities,
818
- );
819
- yield flowResult(
820
- this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.buildEntityHashesIndex(
821
- remoteWorkspaceEntities,
822
- LogEvent.create(
823
- CHANGE_DETECTION_EVENT.CHANGE_DETECTION_LOCAL_HASHES_INDEX_BUILT,
824
- ),
825
- ),
646
+ }
647
+
648
+ openPotentialWorkspacePullConflict(conflict: EntityChangeConflict): void {
649
+ const baseEntityGetter = (
650
+ entityPath: string | undefined,
651
+ ): Entity | undefined =>
652
+ entityPath
653
+ ? this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.entities.find(
654
+ (e) => e.path === entityPath,
655
+ )
656
+ : undefined;
657
+ const currentChangeEntityGetter = (
658
+ entityPath: string | undefined,
659
+ ): Entity | undefined =>
660
+ entityPath
661
+ ? this.editorStore.graphManagerState.graph.allOwnElements
662
+ .map((element) =>
663
+ this.editorStore.graphManagerState.graphManager.elementToEntity(
664
+ element,
665
+ ),
666
+ )
667
+ .find((e) => e.path === entityPath)
668
+ : undefined;
669
+ const incomingChangeEntityGetter = (
670
+ entityPath: string | undefined,
671
+ ): Entity | undefined =>
672
+ entityPath
673
+ ? this.editorStore.changeDetectionState.workspaceRemoteLatestRevisionState.entities.find(
674
+ (e) => e.path === entityPath,
675
+ )
676
+ : undefined;
677
+ const conflictEditorState = new EntityChangeConflictEditorState(
678
+ this.editorStore,
679
+ this.editorStore.conflictResolutionState,
680
+ conflict.entityPath,
681
+ SPECIAL_REVISION_ALIAS.WORKSPACE_BASE,
682
+ SPECIAL_REVISION_ALIAS.LOCAL,
683
+ SPECIAL_REVISION_ALIAS.WORKSPACE_HEAD,
684
+ baseEntityGetter(conflict.entityPath),
685
+ currentChangeEntityGetter(conflict.entityPath),
686
+ incomingChangeEntityGetter(conflict.entityPath),
687
+ baseEntityGetter,
688
+ currentChangeEntityGetter,
689
+ incomingChangeEntityGetter,
690
+ );
691
+ conflictEditorState.setReadOnly(true);
692
+ this.editorStore.tabManagerState.openTab(conflictEditorState);
693
+ }
694
+
695
+ *refreshLocalChanges(): GeneratorFn<void> {
696
+ const startTime = Date.now();
697
+ this.refreshLocalChangesDetectorState.inProgress();
698
+ try {
699
+ // ======= (RE)START CHANGE DETECTION =======
700
+ this.editorStore.changeDetectionState.stop();
701
+ yield Promise.all([
702
+ this.sdlcState.buildWorkspaceLatestRevisionEntityHashesIndex(),
703
+ this.editorStore.changeDetectionState.preComputeGraphElementHashes(),
704
+ ]);
705
+ this.editorStore.changeDetectionState.start();
706
+ this.editorStore.applicationStore.log.info(
707
+ LogEvent.create(CHANGE_DETECTION_EVENT.CHANGE_DETECTION_RESTARTED),
708
+ Date.now() - startTime,
709
+ 'ms',
826
710
  );
827
- yield flowResult(
828
- this.editorStore.changeDetectionState.computeAggregatedWorkspaceRemoteChanges(),
711
+ // ======= FINISHED (RE)START CHANGE DETECTION =======
712
+ } catch (error) {
713
+ assertErrorThrown(error);
714
+ this.editorStore.applicationStore.log.error(
715
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
716
+ error,
829
717
  );
830
- this.editorStore.applicationStore.setActionAlertInfo({
831
- message: 'Local workspace is out-of-sync',
832
- prompt: 'Please pull remote changes before pushing your local changes',
833
- type: ActionAlertType.CAUTION,
834
- actions: [
835
- {
836
- label: 'Pull remote changes',
837
- type: ActionAlertActionType.STANDARD,
838
- default: true,
839
- handler: (): void => {
840
- this.editorStore.setActiveActivity(ACTIVITY_MODE.LOCAL_CHANGES);
841
- flowResult(
842
- this.editorStore.localChangesState.workspaceSyncState.pullChanges(),
843
- ).catch(this.editorStore.applicationStore.alertUnhandledError);
844
- },
845
- },
846
- {
847
- label: 'Cancel',
848
- type: ActionAlertActionType.PROCEED_WITH_CAUTION,
849
- },
850
- ],
851
- });
852
- this.pushChangesState.complete();
853
- return;
718
+ this.editorStore.applicationStore.notifyError(error);
719
+ this.sdlcState.handleChangeDetectionRefreshIssue(error);
720
+ } finally {
721
+ this.refreshLocalChangesDetectorState.complete();
854
722
  }
855
- const currentHashesIndex =
856
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState
857
- .currentEntityHashesIndex;
723
+ }
724
+
725
+ *processConflicts(): GeneratorFn<void> {
858
726
  try {
859
- const nullableRevisionChange =
860
- (yield this.editorStore.sdlcServerClient.performEntityChanges(
861
- this.sdlcState.activeProject.projectId,
862
- this.sdlcState.activeWorkspace,
863
- {
864
- message:
865
- pushMessage ??
866
- `pushed new changes from ${
867
- this.editorStore.applicationStore.config.appName
868
- } [potentially affected ${
869
- this.localChanges.length === 1
870
- ? '1 entity'
871
- : `${this.localChanges.length} entities`
872
- }]`,
873
- entityChanges: this.localChanges,
874
- revisionId: this.sdlcState.activeRevision.id,
875
- },
876
- )) as PlainObject<Revision> | undefined;
877
- const revisionChange = guaranteeNonNullable(
878
- nullableRevisionChange,
879
- `Can't push an empty change set. This may be due to an error with change detection`,
727
+ const isInConflictResolutionMode = (yield flowResult(
728
+ this.sdlcState.checkIfCurrentWorkspaceIsInConflictResolutionMode(),
729
+ )) as boolean;
730
+ if (isInConflictResolutionMode) {
731
+ this.editorStore.applicationStore.setBlockingAlert({
732
+ message: 'Workspace is in conflict resolution mode',
733
+ prompt: 'Please refresh the application',
734
+ });
735
+ return;
736
+ }
737
+ } catch (error) {
738
+ assertErrorThrown(error);
739
+ this.editorStore.applicationStore.notifyWarning(
740
+ 'Failed to check if current workspace is in conflict resolution mode',
880
741
  );
881
- const latestRevision = Revision.serialization.fromJson(revisionChange);
882
- this.sdlcState.setCurrentRevision(latestRevision); // update current revision to the latest
883
- this.sdlcState.setWorkspaceLatestRevision(latestRevision);
884
- const syncFinishedTime = Date.now();
742
+ return;
743
+ }
744
+ }
885
745
 
886
- this.editorStore.applicationStore.log.info(
887
- LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_LOCAL_CHANGES_PUSHED),
888
- syncFinishedTime - startTime,
889
- 'ms',
890
- );
891
- this.localChanges = [];
746
+ getLocalChanges(): EntityChange[] {
747
+ return this.editorStore.graphState.computeLocalEntityChanges();
748
+ }
892
749
 
893
- // ======= (RE)START CHANGE DETECTION =======
894
- try {
895
- /**
896
- * Here we try to rebuild local hash index. If failed, we will use local hash index, but for veracity, it's best to use entities
897
- * coming from the server.
898
- */
899
- const entities =
900
- (yield this.editorStore.sdlcServerClient.getEntitiesByRevision(
901
- this.sdlcState.activeProject.projectId,
902
- this.sdlcState.activeWorkspace,
903
- latestRevision.id,
904
- )) as Entity[];
905
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.setEntities(
906
- entities,
907
- );
908
- yield flowResult(
909
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.buildEntityHashesIndex(
910
- entities,
911
- LogEvent.create(
912
- CHANGE_DETECTION_EVENT.CHANGE_DETECTION_LOCAL_HASHES_INDEX_BUILT,
913
- ),
914
- ),
915
- );
916
- this.editorStore.tabManagerState.refreshCurrentEntityDiffViewer();
917
- } catch (error) {
918
- assertErrorThrown(error);
919
- /**
920
- * NOTE: there is a known problem with the SDLC server where if we try to fetch the entities right after syncing, there is a chance
921
- * that we get entities from the older commit (i.e. potentially some caching issue). As such, to account for this case, we will
922
- * not try to get entities for the workspace HEAD, but for the revision returned from the syncing call (i.e. this must be the latest revision)
923
- * if we get a 404, we will do a refresh and warn user about this. Otherwise, if we get other types of error, we will assume this is a network
924
- * failure and use local workspace hashes index
925
- */
926
- if (error instanceof NetworkClientError) {
927
- if (error.response.status === HttpStatus.NOT_FOUND) {
928
- this.editorStore.applicationStore.log.error(
929
- LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
930
- `Can't fetch entities for the latest workspace revision immediately after syncing`,
931
- error,
932
- );
933
- }
934
- this.editorStore.applicationStore.setActionAlertInfo({
935
- message: `Change detection engine failed to build hashes index for workspace after syncing`,
936
- prompt:
937
- 'To fix this, you can either try to keep refreshing local changes until success or trust and reuse current workspace hashes index',
938
- type: ActionAlertType.CAUTION,
939
- actions: [
940
- {
941
- label: 'Use local hashes index',
942
- type: ActionAlertActionType.PROCEED_WITH_CAUTION,
943
- handler: (): void => {
944
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.setEntityHashesIndex(
945
- currentHashesIndex,
946
- );
947
- this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState.setIsBuildingEntityHashesIndex(
948
- false,
949
- );
950
- },
951
- },
952
- {
953
- label: 'Refresh changes',
954
- type: ActionAlertActionType.STANDARD,
955
- default: true,
956
- handler: this.editorStore.applicationStore.guardUnhandledError(
957
- () => flowResult(this.refreshLocalChanges()),
958
- ),
959
- },
960
- ],
961
- });
962
- } else {
963
- throw error;
964
- }
965
- }
966
- // compute the changes in text mode
750
+ getCurrentHashIndexes(): Map<string, string> {
751
+ return this.editorStore.changeDetectionState.snapshotLocalEntityHashesIndex();
752
+ }
753
+
754
+ stopChangeDetection(): void {
755
+ this.editorStore.changeDetectionState.stop();
756
+ }
757
+
758
+ *restartChangeDetection(): GeneratorFn<void> {
759
+ yield this.editorStore.changeDetectionState.preComputeGraphElementHashes();
760
+ this.editorStore.changeDetectionState.start();
761
+ yield Promise.all([
762
+ this.editorStore.changeDetectionState.computeAggregatedWorkspaceChanges(
763
+ true,
764
+ ),
765
+ ]);
766
+ }
767
+ }
768
+
769
+ export class TextLocalChangesState extends LocalChangesState {
770
+ localChanges: EntityChange[] = [];
771
+
772
+ constructor(editorStore: EditorStore, sdlcState: EditorSDLCState) {
773
+ super(editorStore, sdlcState);
774
+ makeObservable(this, {
775
+ setLocalChanges: action,
776
+ });
777
+ }
778
+
779
+ setLocalChanges(val: EntityChange[]): void {
780
+ this.localChanges = val;
781
+ }
782
+
783
+ *refreshLocalChanges(): GeneratorFn<void> {
784
+ this.refreshLocalChangesDetectorState.inProgress();
785
+ try {
786
+ yield Promise.all([
787
+ this.sdlcState.buildWorkspaceLatestRevisionEntityHashesIndex(),
788
+ this.editorStore.changeDetectionState.preComputeGraphElementHashes(),
789
+ ]);
967
790
  this.editorStore.changeDetectionState.computeLocalChangesInTextMode(
968
791
  this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState
969
792
  .entities,
970
793
  );
971
-
972
- this.editorStore.applicationStore.log.info(
973
- LogEvent.create(CHANGE_DETECTION_EVENT.CHANGE_DETECTION_RESTARTED),
974
- Date.now() - syncFinishedTime,
975
- 'ms',
976
- );
977
- // ======= FINISHED (RE)START CHANGE DETECTION =======
978
794
  } catch (error) {
979
795
  assertErrorThrown(error);
980
796
  this.editorStore.applicationStore.log.error(
981
797
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
982
798
  error,
983
799
  );
984
- if (
985
- error instanceof NetworkClientError &&
986
- error.response.status === HttpStatus.CONFLICT
987
- ) {
988
- // NOTE: a confict here indicates that the reference revision ID sent along with update call
989
- // does not match the HEAD of the workspace, therefore, we need to prompt user to refresh the application
990
- this.editorStore.applicationStore.notifyWarning(
991
- 'Syncing failed. Current workspace revision is not the latest. Please backup your work and refresh the application',
992
- );
993
- // TODO: maybe we should do more here, e.g. prompt the user to download the patch, but that is for later
994
- } else {
995
- this.editorStore.applicationStore.notifyError(error);
996
- }
800
+ this.editorStore.applicationStore.notifyError(error);
801
+ this.sdlcState.handleChangeDetectionRefreshIssue(error);
997
802
  } finally {
998
- this.pushChangesState.complete();
803
+ this.refreshLocalChangesDetectorState.complete();
999
804
  }
1000
805
  }
806
+
807
+ *processConflicts(): GeneratorFn<void> {
808
+ return;
809
+ }
810
+
811
+ getLocalChanges(): EntityChange[] {
812
+ return this.localChanges;
813
+ }
814
+
815
+ getCurrentHashIndexes(): Map<string, string> {
816
+ return this.editorStore.changeDetectionState
817
+ .workspaceLocalLatestRevisionState.currentEntityHashesIndex;
818
+ }
819
+
820
+ stopChangeDetection(): void {
821
+ this.localChanges = [];
822
+ }
823
+
824
+ *restartChangeDetection(): GeneratorFn<void> {
825
+ this.editorStore.changeDetectionState.computeLocalChangesInTextMode(
826
+ this.editorStore.changeDetectionState.workspaceLocalLatestRevisionState
827
+ .entities,
828
+ );
829
+ }
1001
830
  }