This is an automated email from the ASF dual-hosted git repository.

zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 5f235e2ff0 Edit Assets links for adapter while editing (#3817)
5f235e2ff0 is described below

commit 5f235e2ff0883c6253b0d5c3c9dcf27bd5527705
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Mon Oct 13 14:02:23 2025 +0200

    Edit Assets links for adapter while editing (#3817)
    
    Co-authored-by: Philipp Zehnder <[email protected]>
    Co-authored-by: Dominik Riemer <[email protected]>
---
 .../src/lib/model/assets/asset.model.ts            |   1 +
 .../adapter-asset-configuration.component.ts       |  78 +++++++-
 .../start-adapter-configuration.component.html     |   7 +
 .../start-adapter-configuration.component.ts       |  31 ++--
 .../adapter-started-dialog.component.html          |   1 +
 .../adapter-started-dialog.component.ts            |  96 +++++++---
 .../adapter-started-success.component.html         |  22 +++
 .../adapter-started-success.component.ts           |   3 +
 .../adapter-asset-configuration.service.ts         | 205 +++++++++++++++++++--
 9 files changed, 394 insertions(+), 50 deletions(-)

diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts 
b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
index b64f794ec6..e78b42fbb4 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts
@@ -105,6 +105,7 @@ export interface SpAssetModel extends SpAsset {
 export interface SpAssetTreeNode {
     assetId: string;
     assetName: string;
+    assetLinks: AssetLink[];
     assets?: SpAssetTreeNode[];
     spAssetModelId: string;
     flattenPath: any[];
diff --git 
a/ui/src/app/connect/components/adapter-configuration/adapter-asset-configuration/adapter-asset-configuration.component.ts
 
b/ui/src/app/connect/components/adapter-configuration/adapter-asset-configuration/adapter-asset-configuration.component.ts
index f48e53c270..8666dc0958 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/adapter-asset-configuration/adapter-asset-configuration.component.ts
+++ 
b/ui/src/app/connect/components/adapter-configuration/adapter-asset-configuration/adapter-asset-configuration.component.ts
@@ -26,6 +26,7 @@ import {
     AssetLinkType,
     SpAsset,
     SpAssetTreeNode,
+    AdapterDescription,
 } from '@streampipes/platform-services';
 import { MatStepper } from '@angular/material/stepper';
 import { Observable } from 'rxjs';
@@ -39,11 +40,15 @@ import { Observable } from 'rxjs';
 export class AdapterAssetConfigurationComponent implements OnInit {
     @Input() linkageData: LinkageData[] = [];
     @Input() stepper: MatStepper;
+    @Input() isEdit: boolean;
+    @Input() adapter: AdapterDescription;
 
     @Output() adapterStartedEmitter: EventEmitter<void> =
         new EventEmitter<void>();
 
     @Output() selectedAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
+    @Output() deselectedAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
+    @Output() originalAssetsEmitter = new EventEmitter<SpAssetTreeNode[]>();
 
     treeControl: NestedTreeControl<SpAssetTreeNode>;
     dataSource: MatTreeNestedDataSource<SpAssetTreeNode>;
@@ -56,6 +61,8 @@ export class AdapterAssetConfigurationComponent implements 
OnInit {
     assetLinksLoaded = false;
     updateObservable: Observable<SpAssetModel>;
     selectedAssets: SpAssetTreeNode[] = [];
+    deselectedAssets: SpAssetTreeNode[] = [];
+    originalAssets: SpAssetTreeNode[] = [];
 
     constructor(private assetService: AssetManagementService) {
         this.treeControl = new NestedTreeControl<SpAssetTreeNode>(
@@ -76,13 +83,40 @@ export class AdapterAssetConfigurationComponent implements 
OnInit {
             asset => asset.assetId === node.assetId,
         );
 
+        const index_deselected = this.deselectedAssets.findIndex(
+            asset => asset.assetId === node.assetId,
+        );
+
         if (index > -1) {
             this.selectedAssets.splice(index, 1);
+            if (this.isNodeInOriginalData(node)) {
+                this.deselectedAssets.push(node);
+            }
         } else {
             this.selectedAssets.push(node);
+            if (index_deselected > -1) {
+                this.deselectedAssets.splice(index_deselected, 1);
+            }
         }
 
-        this.selectedAssetsChange.emit(this.selectedAssets);
+        const selectEmit = this.selectedAssets.filter(
+            node => !this.isNodeInOriginalData(node),
+        );
+
+        this.selectedAssetsChange.emit(selectEmit);
+        this.deselectedAssetsChange.emit(this.deselectedAssets);
+    }
+
+    private isNodeInOriginalData(node: SpAssetTreeNode): boolean {
+        for (const asset of this.originalAssets) {
+            if (
+                asset.assetId === node.assetId &&
+                asset.spAssetModelId === node.spAssetModelId
+            ) {
+                return true;
+            }
+        }
+        return false;
     }
 
     isSelected(node: SpAssetTreeNode): boolean {
@@ -100,9 +134,50 @@ export class AdapterAssetConfigurationComponent implements 
OnInit {
             next: assets => {
                 this.assetsData = this.mapAssets(assets);
                 this.dataSource.data = this.assetsData;
+                if (this.isEdit) {
+                    this.setSelect();
+                }
             },
         });
     }
+
+    private setSelect() {
+        if (!this.adapter || !this.adapter.elementId) {
+            return;
+        }
+
+        this.assetsData.forEach(node => {
+            this.selectNodeIfMatch(node);
+        });
+    }
+
+    private selectNodeIfMatch(
+        node: SpAssetTreeNode,
+        path: SpAssetTreeNode[] = [],
+    ) {
+        const currentPath = [...path, node];
+
+        if (
+            node.assetLinks &&
+            node.assetLinks.some(
+                link => link.resourceId === this.adapter.elementId,
+            )
+        ) {
+            if (!this.isSelected(node)) {
+                this.selectedAssets.push(node);
+                this.originalAssets.push(node);
+                this.originalAssetsEmitter.emit(this.originalAssets);
+                currentPath.forEach(n => this.treeControl.expand(n));
+            }
+        }
+
+        if (node.assets) {
+            node.assets.forEach(child =>
+                this.selectNodeIfMatch(child, currentPath),
+            );
+        }
+    }
+
     private mapAssets(
         apiAssets: SpAsset[],
         parentId: string = '',
@@ -124,6 +199,7 @@ export class AdapterAssetConfigurationComponent implements 
OnInit {
                 assetId: asset.assetId,
                 assetName: asset.assetName,
                 flattenPath: flattenedPath,
+                assetLinks: asset.assetLinks,
                 assets: asset.assets
                     ? this.mapAssets(asset.assets, parentId, flattenedPathCopy)
                     : [],
diff --git 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
index fe2fd92658..4f9c96965e 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
+++ 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.html
@@ -108,11 +108,18 @@
             [optionDescription]="'Add Adapter to an existing Asset' | 
translate"
             optionIcon="precision_manufacturing"
             dataCy="show-asset-checkbox"
+            [isChecked]="
+                showAsset && (!isEditMode || originalAssets.length > 0)
+            "
             (optionSelectedEmitter)="showAsset = $event"
         >
             <sp-adapter-asset-configuration
                 *ngIf="showAsset"
+                [isEdit]="isEditMode"
+                [adapter]="adapterDescription"
                 (selectedAssetsChange)="onSelectedAssetsChange($event)"
+                (deselectedAssetsChange)="onDeselectedAssetsChange($event)"
+                (originalAssetsEmitter)="onOriginalAssetsEmitted($event)"
             >
             </sp-adapter-asset-configuration>
         </sp-adapter-options-panel>
diff --git 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
index 476877c11c..5d2ccbaf39 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
+++ 
b/ui/src/app/connect/components/adapter-configuration/start-adapter-configuration/start-adapter-configuration.component.ts
@@ -16,20 +16,12 @@
  *
  */
 
-import {
-    Component,
-    EventEmitter,
-    Input,
-    OnInit,
-    Output,
-    SimpleChanges,
-    OnChanges,
-} from '@angular/core';
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import {
     AdapterDescription,
-    SpAssetTreeNode,
     EventRateTransformationRuleDescription,
     EventSchema,
+    SpAssetTreeNode,
     RemoveDuplicatesTransformationRuleDescription,
 } from '@streampipes/platform-services';
 import {
@@ -67,8 +59,6 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
 
     @Input() isEditMode: boolean;
 
-    @Input() stepper: MatStepper;
-
     /**
      * Cancels the adapter configuration process
      */
@@ -110,6 +100,8 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
     showCode = false;
     showAsset = false;
     selectedAssets = [];
+    deselectedAssets = [];
+    originalAssets = [];
 
     constructor(
         private dialogService: DialogService,
@@ -120,6 +112,7 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
     ) {}
 
     ngOnInit(): void {
+        this.showAsset = this.isEditMode;
         this.startAdapterForm = this._formBuilder.group({});
         this.startAdapterForm.addControl(
             'adapterName',
@@ -189,6 +182,9 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
             data: {
                 adapter: this.adapterDescription,
                 editMode: true,
+                selectedAssets: this.selectedAssets,
+                deselectedAssets: this.deselectedAssets,
+                originalAssets: this.originalAssets,
             },
         });
 
@@ -213,8 +209,7 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
                 selectedAssets: this.selectedAssets,
             },
         });
-        const dialogInstance =
-            dialogRef.componentInstance as unknown as AdapterStartedDialog;
+
         dialogRef.afterClosed().subscribe(() => {
             this.adapterStartedEmitter.emit();
         });
@@ -224,6 +219,14 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
         this.selectedAssets = updatedAssets;
     }
 
+    onDeselectedAssetsChange(updatedAssets: SpAssetTreeNode[]): void {
+        this.deselectedAssets = updatedAssets;
+    }
+
+    onOriginalAssetsEmitted(updatedAssets: SpAssetTreeNode[]): void {
+        this.originalAssets = updatedAssets;
+    }
+
     private checkAndApplyStreamRules(): void {
         if (this.removeDuplicates) {
             const removeDuplicates: 
RemoveDuplicatesTransformationRuleDescription =
diff --git 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.html
 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.html
index 0f01497bc3..8d0560d4e7 100644
--- 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.html
+++ 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.html
@@ -45,6 +45,7 @@
                     [pipelineOperationStatus]="pipelineOperationStatus"
                     [saveInDataLake]="saveInDataLake"
                     [saveInAsset]="addToAssetText"
+                    [deletedFromAsset]="deletedFromAssetText"
                     [templateErrorMessage]="templateErrorMessage"
                     [adapterErrorMessage]="adapterErrorMessage"
                 ></sp-adapter-started-success>
diff --git 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.ts 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.ts
index 7193931339..8f16df7572 100644
--- 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.ts
+++ 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-dialog.component.ts
@@ -24,6 +24,7 @@ import {
     Output,
     inject,
 } from '@angular/core';
+
 import { ShepherdService } from '../../../services/tour/shepherd.service';
 import {
     AdapterDescription,
@@ -38,16 +39,14 @@ import {
     PipelineTemplateService,
     PipelineUpdateInfo,
     SpLogMessage,
-} from '@streampipes/platform-services';
-import { DialogRef } from '@streampipes/shared-ui';
-import {
-    CompactPipelineService,
     LinkageData,
+    CompactPipelineService,
 } from '@streampipes/platform-services';
-import { AssetSaveService } from 
'../../services/adapter-asset-configuration.service';
+import { DialogRef } from '@streampipes/shared-ui';
 
-import { firstValueFrom } from 'rxjs';
 import { TranslateService } from '@ngx-translate/core';
+import { AssetSaveService } from 
'../../services/adapter-asset-configuration.service';
+import { firstValueFrom } from 'rxjs';
 
 @Component({
     selector: 'sp-dialog-adapter-started-dialog',
@@ -70,6 +69,8 @@ export class AdapterStartedDialog implements OnInit {
      * Assets selectedAsset to link the adapter tp
      */
     @Input() selectedAssets: SpAssetTreeNode[];
+    @Input() deselectedAssets: SpAssetTreeNode[];
+    @Input() originalAssets: SpAssetTreeNode[];
 
     /**
      * Indicates if a pipeline to store the adapter events should be started
@@ -107,6 +108,7 @@ export class AdapterStartedDialog implements OnInit {
     adapterElementId = '';
     adapterErrorMessage: SpLogMessage;
     addToAssetText = '';
+    deletedFromAssetText = '';
 
     constructor(
         public dialogRef: DialogRef<AdapterStartedDialog>,
@@ -150,6 +152,13 @@ export class AdapterStartedDialog implements OnInit {
             },
         );
 
+        this.loadingText = this.translateService.instant(
+            'Updating adapter {{adapterName}}',
+            {
+                adapterName: this.adapter.name,
+            },
+        );
+
         this.loading = true;
         this.adapterService.updateAdapter(this.adapter).subscribe({
             next: status => {
@@ -162,6 +171,8 @@ export class AdapterStartedDialog implements OnInit {
 
                     this.onAdapterFailure(errorLogMessage);
                 }
+
+                this.addToAsset();
             },
             error: error => {
                 this.onAdapterFailure(error.error);
@@ -170,6 +181,12 @@ export class AdapterStartedDialog implements OnInit {
     }
 
     addAdapter() {
+        this.loadingText = this.translateService.instant(
+            'Creating adapter {{adapterName}}',
+            {
+                adapterName: this.adapter.name,
+            },
+        );
         this.loadingText = this.translateService.instant(
             'Creating adapter {{adapterName}}',
             {
@@ -182,11 +199,13 @@ export class AdapterStartedDialog implements OnInit {
                 if (status.success) {
                     const adapterElementId = status.notifications[0].title;
                     this.adapterElementId = adapterElementId;
+                    this.adapterElementId = adapterElementId;
                     if (this.saveInDataLake) {
                         this.startSaveInDataLakePipeline(adapterElementId);
                     } else {
                         this.startAdapter(adapterElementId, true);
                         this.addToAsset();
+                        this.addToAsset();
                     }
                 } else {
                     const errorMsg: SpLogMessage =
@@ -226,10 +245,16 @@ export class AdapterStartedDialog implements OnInit {
                     adapterName: this.adapter.name,
                 },
             );
+            this.loadingText = this.translateService.instant(
+                'Starting adapter {{adapterName}}',
+                {
+                    adapterName: this.adapter.name,
+                },
+            );
             this.adapterService
                 .startAdapterByElementId(adapterElementId)
                 .subscribe(
-                    startStatus => {
+                    () => {
                         this.onAdapterReady(successMessage, showPreview);
                     },
                     error => {
@@ -264,15 +289,21 @@ export class AdapterStartedDialog implements OnInit {
     }
 
     async addToAsset(): Promise<void> {
+        let linkageData: LinkageData[];
         try {
-            const adapter = await this.getAdapter();
-            const linkageData: LinkageData[] = this.createLinkageData(adapter);
+            if (!this.editMode) {
+                const adapter = await this.getAdapter();
+                linkageData = this.createLinkageData(adapter);
 
-            if (this.saveInDataLake) {
-                await this.addDataLakeLinkageData(adapter, linkageData);
+                if (this.saveInDataLake) {
+                    await this.addDataLakeLinkageData(adapter, linkageData);
+                }
+            } else {
+                linkageData = this.createLinkageData(this.adapter);
             }
 
             await this.saveAssets(linkageData);
+
             this.setSuccessMessage(linkageData);
         } catch (err) {
             console.error('Error in addToAsset:', err);
@@ -289,7 +320,10 @@ export class AdapterStartedDialog implements OnInit {
         return [
             {
                 type: 'adapter',
-                id: this.adapterElementId,
+                id:
+                    this.adapterElementId !== ''
+                        ? this.adapterElementId
+                        : adapter.elementId,
                 name: adapter.name,
             },
             {
@@ -326,6 +360,8 @@ export class AdapterStartedDialog implements OnInit {
         await this.assetSaveService.saveSelectedAssets(
             this.selectedAssets,
             linkageData,
+            this.deselectedAssets,
+            this.originalAssets,
         );
     }
 
@@ -333,18 +369,31 @@ export class AdapterStartedDialog implements OnInit {
         const assetTypesList = this.formatWithAnd(
             linkageData.map(data => data.type),
         );
+        if (this.selectedAssets.length > 0) {
+            const assetIdsList = this.formatWithAnd(
+                this.selectedAssets.map(asset => asset.assetName),
+            );
 
-        const assetIdsList = this.formatWithAnd(
-            this.selectedAssets.map(asset => asset.assetName),
-        );
-
-        this.addToAssetText = this.translateService.instant(
-            'Your {{assetTypes}} were successfully added to {{assetIds}}.',
-            {
-                assetTypes: assetTypesList,
-                assetIds: assetIdsList,
-            },
-        );
+            this.addToAssetText = this.translateService.instant(
+                'Your {{assetTypes}} were successfully added to {{assetIds}}.',
+                {
+                    assetTypes: assetTypesList,
+                    assetIds: assetIdsList,
+                },
+            );
+        }
+        if (this.deselectedAssets && this.deselectedAssets.length > 0) {
+            const assetIdsRemovedList = this.formatWithAnd(
+                this.deselectedAssets.map(asset => asset.assetName),
+            );
+            this.deletedFromAssetText = this.translateService.instant(
+                'Your {{assetTypes}} were successfully deleted from 
{{assetIds}}.',
+                {
+                    assetTypes: assetTypesList,
+                    assetIds: assetIdsRemovedList,
+                },
+            );
+        }
     }
 
     private formatWithAnd(list: string[]): string {
@@ -381,6 +430,7 @@ export class AdapterStartedDialog implements OnInit {
                                     pipelineOperationStatus;
                                 this.startAdapter(adapterElementId, true);
                                 this.addToAsset();
+                                this.addToAsset();
                             },
                             error => {
                                 this.onAdapterFailure(error.error);
diff --git 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.html
 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.html
index 1e3dd059d5..eacf8df178 100644
--- 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.html
+++ 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.html
@@ -87,6 +87,28 @@
         </div>
         <mat-divider fxFlex="100"></mat-divider>
     </div>
+
+    <div
+        fxLayout="column"
+        fxLayoutAlign="center center"
+        fxFlex="100"
+        *ngIf="deletedFromAsset !== ''"
+    >
+        <div>
+            <div
+                class="info-message"
+                fxFlex="100"
+                fxLayoutAlign="center center"
+                fxLayout="row"
+            >
+                <i class="material-icons">done</i>
+                <span data-cy="sp-connect-adapter-success-added"
+                    >&nbsp;{{ deletedFromAsset }}</span
+                >
+            </div>
+        </div>
+        <mat-divider fxFlex="100"></mat-divider>
+    </div>
     <sp-exception-details
         *ngIf="adapterErrorMessage"
         [message]="adapterErrorMessage"
diff --git 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.ts
 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.ts
index 51fd017df1..2609cc670c 100644
--- 
a/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.ts
+++ 
b/ui/src/app/connect/dialog/adapter-started/adapter-started-success/adapter-started-success.component.ts
@@ -39,6 +39,9 @@ export class SpAdapterStartedSuccessComponent {
     @Input()
     saveInAsset = '';
 
+    @Input()
+    deletedFromAsset = '';
+
     @Input()
     pipelineOperationStatus: PipelineOperationStatus;
 
diff --git a/ui/src/app/connect/services/adapter-asset-configuration.service.ts 
b/ui/src/app/connect/services/adapter-asset-configuration.service.ts
index 67d374b730..c7dbe9ef9a 100644
--- a/ui/src/app/connect/services/adapter-asset-configuration.service.ts
+++ b/ui/src/app/connect/services/adapter-asset-configuration.service.ts
@@ -27,6 +27,7 @@ import {
     GenericStorageService,
     SpAssetTreeNode,
 } from '@streampipes/platform-services';
+import { firstValueFrom } from 'rxjs';
 
 @Injectable({
     providedIn: 'root',
@@ -44,11 +45,55 @@ export class AssetSaveService {
     @Output() adapterStartedEmitter: EventEmitter<void> =
         new EventEmitter<void>();
 
-    saveSelectedAssets(
+    async saveSelectedAssets(
         selectedAssets: SpAssetTreeNode[],
         linkageData: LinkageData[],
-    ): void {
-        const uniqueAssetIDsDict = this.getAssetPaths(selectedAssets);
+        deselectedAssets: SpAssetTreeNode[] = [],
+        originalAssets: SpAssetTreeNode[] = [],
+    ): Promise<void> {
+        const links = this.buildLinks(linkageData);
+
+        if (deselectedAssets.length > 0) {
+            await this.deleteLinkOnDeselectAssets(deselectedAssets, links);
+        }
+        if (selectedAssets.length > 0) {
+            await this.setLinkOnSelectAssets(selectedAssets, links);
+        }
+
+        if (originalAssets.length > 0) {
+            //filter is necessary, otherwise conflicting database instances 
are produced
+            const filteredOriginal = this.filterAssets(
+                originalAssets,
+                deselectedAssets,
+                selectedAssets,
+            );
+
+            if (filteredOriginal.length > 0) {
+                this.renameLinkage(filteredOriginal, links);
+            }
+        }
+    }
+    private filterAssets(
+        originalAssets: SpAssetTreeNode[],
+        deselectedAssets: SpAssetTreeNode[],
+        selectedAssets: SpAssetTreeNode[],
+    ): SpAssetTreeNode[] {
+        const deselectedAssetIds = new Set(
+            deselectedAssets.map(asset => asset.assetId),
+        );
+        const selectedAssetIds = new Set(
+            selectedAssets.map(asset => asset.assetId),
+        );
+
+        return originalAssets.filter(
+            asset =>
+                !deselectedAssetIds.has(asset.assetId) &&
+                !selectedAssetIds.has(asset.assetId),
+        );
+    }
+
+    renameLinkage(originalAssets, links) {
+        const uniqueAssetIDsDict = this.getAssetPaths(originalAssets);
         const uniqueAssetIDs = Object.keys(uniqueAssetIDsDict);
 
         uniqueAssetIDs.forEach(spAssetModelId => {
@@ -56,24 +101,37 @@ export class AssetSaveService {
                 next: current => {
                     this.currentAsset = current;
 
-                    const links = this.buildLinks(linkageData);
-
                     uniqueAssetIDsDict[spAssetModelId].forEach(path => {
                         if (path.length === 2) {
-                            current.assetLinks = [
-                                ...(current.assetLinks ?? []),
-                                ...links,
-                            ];
+                            current.assetLinks = (current.assetLinks ?? 
[]).map(
+                                (link: any) => {
+                                    const matchedLink = links.find(
+                                        l => l.resourceId === link.resourceId,
+                                    );
+                                    if (matchedLink) {
+                                        link.linkLabel = matchedLink.linkLabel;
+                                    }
+                                    return link;
+                                },
+                            );
                         }
+
                         if (path.length > 2) {
-                            this.updateDictValue(current, path, links);
+                            links.forEach(linkToUpdate => {
+                                this.updateLinkLabelInDict(
+                                    current,
+                                    path,
+                                    linkToUpdate,
+                                );
+                            });
                         }
                     });
 
                     const updateObservable =
                         this.assetService.updateAsset(current);
+
                     updateObservable?.subscribe({
-                        next: updated => {
+                        next: () => {
                             this.adapterStartedEmitter.emit();
                         },
                     });
@@ -82,6 +140,130 @@ export class AssetSaveService {
         });
     }
 
+    private updateLinkLabelInDict(
+        dict: SpAssetTreeNode,
+        path: (string | number)[],
+        linkToUpdate: any,
+    ) {
+        let current = dict;
+
+        for (let i = 2; i < path.length; i++) {
+            const key = path[i];
+            if (i === path.length - 1) {
+                if (current.assets?.[key]?.assetLinks) {
+                    current.assets[key].assetLinks = current.assets[
+                        key
+                    ].assetLinks.map((link: any) => {
+                        if (link.resourceId === linkToUpdate.resourceId) {
+                            link.linkLabel = linkToUpdate.linkLabel;
+                        }
+                        return link;
+                    });
+                }
+            } else {
+                if (Array.isArray(current.assets)) {
+                    current = current.assets[key as number];
+                }
+            }
+        }
+
+        return current;
+    }
+    async setLinkOnSelectAssets(
+        selectedAssets: SpAssetTreeNode[],
+        links: AssetLink[],
+    ): Promise<void> {
+        const uniqueAssetIDsDict = this.getAssetPaths(selectedAssets);
+        const uniqueAssetIDs = Object.keys(uniqueAssetIDsDict);
+
+        for (const spAssetModelId of uniqueAssetIDs) {
+            const current = await firstValueFrom(
+                this.assetService.getAsset(spAssetModelId),
+            );
+
+            uniqueAssetIDsDict[spAssetModelId].forEach(path => {
+                if (path.length === 2) {
+                    current.assetLinks = [
+                        ...(current.assetLinks ?? []),
+                        ...links,
+                    ];
+                }
+
+                if (path.length > 2) {
+                    this.updateDictValue(current, path, links);
+                }
+            });
+
+            const updateObservable = this.assetService.updateAsset(current);
+            await firstValueFrom(updateObservable); // Ensure this completes 
before continuing
+        }
+    }
+
+    async deleteLinkOnDeselectAssets(
+        deselectedAssets: SpAssetTreeNode[],
+        links: AssetLink[],
+    ): Promise<void> {
+        const uniqueAssetIDsDict = this.getAssetPaths(deselectedAssets);
+        const uniqueAssetIDs = Object.keys(uniqueAssetIDsDict);
+
+        for (const spAssetModelId of uniqueAssetIDs) {
+            const current = await firstValueFrom(
+                this.assetService.getAsset(spAssetModelId),
+            );
+
+            uniqueAssetIDsDict[spAssetModelId].forEach(path => {
+                if (path.length === 2) {
+                    current.assetLinks = (current.assetLinks ?? []).filter(
+                        (link: any) =>
+                            !links.some(
+                                l =>
+                                    JSON.stringify(l.resourceId) ===
+                                    JSON.stringify(link.resourceId),
+                            ),
+                    );
+                }
+
+                if (path.length > 2) {
+                    links.forEach(linkToRemove => {
+                        this.deleteDictValue(current, path, linkToRemove);
+                    });
+                }
+            });
+
+            const updateObservable = this.assetService.updateAsset(current);
+            await firstValueFrom(updateObservable); // Ensure this completes 
before continuing
+        }
+    }
+
+    private deleteDictValue(
+        dict: SpAssetTreeNode,
+        path: (string | number)[],
+        linkToRemove: any,
+    ) {
+        let current = dict;
+
+        for (let i = 2; i < path.length; i++) {
+            const key = path[i];
+            if (i === path.length - 1) {
+                if (current.assets?.[key]?.assetLinks) {
+                    current.assets[key].assetLinks = current.assets[
+                        key
+                    ].assetLinks.filter(
+                        (link: any) =>
+                            JSON.stringify(link.resourceId) !==
+                            JSON.stringify(linkToRemove.resourceId),
+                    );
+                }
+            } else {
+                if (Array.isArray(current.assets)) {
+                    current = current.assets[key as number];
+                }
+            }
+        }
+
+        return current;
+    }
+
     private updateDictValue(
         dict: SpAssetModel,
         path: (string | number)[],
@@ -89,7 +271,6 @@ export class AssetSaveService {
     ) {
         const result: any = { ...dict };
         let current = result;
-        let parent: any = null;
         for (let i = 2; i < path.length; i++) {
             const key = path[i];
 

Reply via email to