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

tenthe 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 e071909262 1358 harmonize resource creation workflow for connect 
(#4707)
e071909262 is described below

commit e071909262bc5b1000f503df086d85bf09637f9c
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Thu Jul 23 22:55:06 2026 +0200

    1358 harmonize resource creation workflow for connect (#4707)
    
    Co-authored-by: Philipp Zehnder <[email protected]>
---
 ui/deployment/i18n/de.json                         |   5 +-
 ui/deployment/i18n/en.json                         |   5 +-
 ui/deployment/i18n/pl.json                         |   5 +-
 .../object-manage-dialog.component.html            |   1 -
 .../object-manage-dialog.component.ts              |  24 ++-
 .../adapter-configuration.component.html           |  36 ++++-
 .../adapter-configuration.component.ts             | 121 ++++++++++++++
 .../start-adapter-configuration.component.html     | 179 ++++++++++-----------
 .../start-adapter-configuration.component.ts       |  13 +-
 .../existing-adapters.component.html               |  16 +-
 .../existing-adapters.component.ts                 | 106 +++++++++---
 .../adapter-code-dialog.component.html             |  37 +++++
 .../adapter-code-dialog.component.ts               |  50 ++++++
 .../adapter-started-dialog.component.html          |  27 ++--
 .../adapter-started-dialog.component.ts            |  32 +++-
 15 files changed, 499 insertions(+), 158 deletions(-)

diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index 76a652ae74..e60235e3e3 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -644,6 +644,7 @@
   "Mail Template": "Mail-Vorlage",
   "Make available to all users with appropriate role": "Allen Benutzern mit 
entsprechender Rolle zur Verfügung stellen",
   "Manage": "Verwalten",
+  "Manage Adapter ": "Adapter verwalten ",
   "Manage Asset ": "Asset verwalten ",
   "Manage Chart ": "Diagramm verwalten",
   "Manage Dashboard ": "Dashboard verwalten",
@@ -656,7 +657,6 @@
   "Manage datasets": "Datensätze verwalten",
   "Manage links": "Verwalten von Links",
   "Manage permissions": "Berechtigungen verwalten",
-  "Manage permissions for adapter ": "Berechtigungen für Adapter verwalten",
   "Manage permissions for dataset ": "Berechtigungen für Datensatz verwalten",
   "Manage permissions for pipeline element {{name}}": "Berechtigungen für 
Pipeline-Element {{name}} verwalten",
   "Manage pipeline": "Pipeline verwalten",
@@ -1001,9 +1001,8 @@
   "Show Last Seen Timestamp": "Letzten Zeitstempel anzeigen",
   "Show Pointer": "Zeiger anzeigen",
   "Show Value in Traffic Light": "Wert anzeigen",
+  "Show as Code": "Code anzeigen",
   "Show asset": "Asset anzeigen",
-  "Show code": "Code anzeigen",
-  "Show code to programmatically deploy this adapter over the API": "Code 
anzeigen, um diesen Adapter programmatisch über die API einzusetzen",
   "Show delta indicator": "Delta-Indikator anzeigen",
   "Show details": "Details anzeigen",
   "Show documentation": "Dokumentation anzeigen",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index f0b7ebadf8..198792fa61 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -644,6 +644,7 @@
   "Mail Template": null,
   "Make available to all users with appropriate role": null,
   "Manage": null,
+  "Manage Adapter ": null,
   "Manage Asset ": null,
   "Manage Chart ": null,
   "Manage Dashboard ": null,
@@ -656,7 +657,6 @@
   "Manage datasets": null,
   "Manage links": null,
   "Manage permissions": null,
-  "Manage permissions for adapter ": null,
   "Manage permissions for dataset ": null,
   "Manage permissions for pipeline element {{name}}": "Manage permissions for 
pipeline element {{name}}",
   "Manage pipeline": null,
@@ -1001,9 +1001,8 @@
   "Show Last Seen Timestamp": null,
   "Show Pointer": null,
   "Show Value in Traffic Light": null,
+  "Show as Code": null,
   "Show asset": null,
-  "Show code": null,
-  "Show code to programmatically deploy this adapter over the API": null,
   "Show delta indicator": null,
   "Show details": null,
   "Show documentation": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index dab204a19c..00b80f0417 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -644,6 +644,7 @@
   "Mail Template": "Szablon wiadomości",
   "Make available to all users with appropriate role": "Udostępnij wszystkim 
użytkownikom z odpowiednią rolą",
   "Manage": "Zarządzanie",
+  "Manage Adapter ": "Zarządzaj adapterem ",
   "Manage Asset ": "Zarządzanie aktywami ",
   "Manage Chart ": "Zarządzaj wykresem",
   "Manage Dashboard ": "Zarządzaj pulpitem nawigacyjnym",
@@ -656,7 +657,6 @@
   "Manage datasets": "Zarządzaj zestawami danych",
   "Manage links": "Zarządzaj linkami",
   "Manage permissions": "Zarządzaj uprawnieniami",
-  "Manage permissions for adapter ": "Zarządzaj uprawnieniami dla adaptera ",
   "Manage permissions for dataset ": "Zarządzaj uprawnieniami dla zbioru 
danych ",
   "Manage permissions for pipeline element {{name}}": "Zarządzaj uprawnieniami 
dla elementu strumienia {{name}}",
   "Manage pipeline": "Zarządzaj procesem",
@@ -1001,9 +1001,8 @@
   "Show Last Seen Timestamp": "Pokaż znacznik czasu ostatniego wystąpienia",
   "Show Pointer": "Pokaż wskaźnik",
   "Show Value in Traffic Light": "Pokaż wartość na sygnalizatorze",
+  "Show as Code": "Pokaż jako kod",
   "Show asset": "Pokaż zasób",
-  "Show code": "Pokaż kod",
-  "Show code to programmatically deploy this adapter over the API": "Pokaż kod 
do programowego wdrożenia tego adaptera przez API",
   "Show delta indicator": "Pokaż wskaźnik zmiany",
   "Show details": "Pokaż szczegóły",
   "Show documentation": "Pokaż dokumentację",
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.html
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.html
index ae3483a221..1d35f625b8 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.html
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.html
@@ -26,7 +26,6 @@
     } @else if (!permissionDenied || createMode) {
         <div class="sp-dialog-content">
             <div fxFlex="100" fxLayout="column" class="p-15">
-                <h4>{{ headerTitle }}</h4>
                 <form [formGroup]="parentForm" fxFlex="100" fxLayout="column">
                     @if (showResourceSection) {
                         <sp-split-section
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.ts
index 37d3e09722..9f2e97be27 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/object-manage-dialog/object-manage-dialog.component.ts
@@ -398,8 +398,10 @@ export class ObjectManageDialogComponent<
             return;
         }
 
-        await this.saveImmediately(result);
-        this.close(true);
+        const saved = await this.saveImmediately(result);
+        if (saved) {
+            this.close(true);
+        }
     }
 
     close(result?: ObjectManageDialogResult<TResource> | boolean) {
@@ -433,9 +435,13 @@ export class ObjectManageDialogComponent<
 
     private async saveImmediately(
         result: ObjectManageDialogResult<TResource>,
-    ): Promise<void> {
+    ): Promise<boolean> {
         this.touchResource(result.resource);
-        await this.saveResource(result.resource);
+        const saved = await this.saveResource(result.resource);
+        if (!saved) {
+            return false;
+        }
+
         if (result.permission) {
             await firstValueFrom(
                 this.permissionsService.updatePermission(result.permission),
@@ -450,6 +456,8 @@ export class ObjectManageDialogComponent<
                 result.originalAssets,
             );
         }
+
+        return true;
     }
 
     private createLinkageData(resource: TResource): LinkageData[] {
@@ -480,17 +488,17 @@ export class ObjectManageDialogComponent<
         );
     }
 
-    private async saveResource(resource: TResource): Promise<void> {
+    private async saveResource(resource: TResource): Promise<boolean> {
         const saveResource = this.resourceConfig.saveResource;
         if (!saveResource) {
-            return;
+            return true;
         }
 
         const saveResult = saveResource(resource);
         if (isObservable(saveResult)) {
-            await firstValueFrom(saveResult);
+            return (await firstValueFrom(saveResult)) !== false;
         } else {
-            await saveResult;
+            return (await saveResult) !== false;
         }
     }
 
diff --git 
a/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.html
 
b/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.html
index 6379c07b41..991936ae86 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.html
+++ 
b/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.html
@@ -21,7 +21,36 @@
     [showBackLink]="true"
     [backLinkTarget]="['connect']"
 >
-    <div nav fxFlex="100" fxLayout="row" fxLayoutAlign="end center"></div>
+    <div nav fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
+        @if (isEditMode) {
+            <button
+                mat-icon-button
+                [matMenuTriggerFor]="optMenu"
+                [attr.aria-label]="'Options' | translate"
+                data-cy="options-adapter"
+            >
+                <mat-icon>more_vert</mat-icon>
+            </button>
+            <mat-menu #optMenu="matMenu">
+                <button
+                    mat-menu-item
+                    (click)="manageAdapter()"
+                    data-cy="manage-adapter-btn"
+                >
+                    <mat-icon>settings</mat-icon>
+                    <span>{{ 'Manage' | translate }}</span>
+                </button>
+                <button
+                    mat-menu-item
+                    (click)="deleteAdapter()"
+                    data-cy="delete-adapter-btn"
+                >
+                    <mat-icon>clear</mat-icon>
+                    <span>{{ 'Delete' | translate }}</span>
+                </button>
+            </mat-menu>
+        }
+    </div>
     @let adapterConfigurationState = state();
     @if (adapterConfigurationState) {
         @if (adapterConfigurationState.adapterDescription) {
@@ -97,6 +126,11 @@
                                 adapterDescription.dataStream.eventSchema
                             "
                             [isEditMode]="isEditMode"
+                            [permission]="pendingPermission"
+                            [selectedAssets]="pendingSelectedAssets"
+                            [deselectedAssets]="pendingDeselectedAssets"
+                            [originalAssets]="pendingOriginalAssets"
+                            [addToAssets]="shouldAddToAssets"
                             (cancelEmitter)="navigateToAdapterCatalog()"
                             (goBackEmitter)="goBack()"
                             (adapterStartedEmitter)="adapterWasStarted()"
diff --git 
a/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.ts
 
b/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.ts
index e8947198e8..90e5c338da 100644
--- 
a/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.ts
+++ 
b/ui/src/app/connect/components/adapter-configuration/adapter-configuration.component.ts
@@ -31,6 +31,11 @@ import { Router } from '@angular/router';
 import { TranslatePipe, TranslateService } from '@ngx-translate/core';
 import { AdapterConfigurationStateService } from 
'./adapter-configuration-state-service/adapter-configuration-state.service';
 import {
+    DialogService,
+    ObjectManageDialogComponent,
+    ObjectManageDialogResourceConfig,
+    ObjectManageDialogResult,
+    PanelType,
     SpBasicHeaderTitleComponent,
     SpBasicViewComponent,
 } from '@streampipes/shared-ui';
@@ -43,6 +48,10 @@ import { AdapterSettingsComponent } from 
'./adapter-settings/adapter-settings.co
 import { ConfigureSchemaComponent } from 
'./configure-schema/configure-schema.component';
 import { ConfigureFieldsComponent } from 
'./configure-fields/configure-fields.component';
 import { StartAdapterConfigurationComponent } from 
'./start-adapter-configuration/start-adapter-configuration.component';
+import { MatIconButton } from '@angular/material/button';
+import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
+import { MatIcon } from '@angular/material/icon';
+import { DeleteAdapterDialogComponent } from 
'../../dialog/delete-adapter-dialog/delete-adapter-dialog.component';
 
 @Component({
     selector: 'sp-adapter-configuration',
@@ -54,6 +63,11 @@ import { StartAdapterConfigurationComponent } from 
'./start-adapter-configuratio
         LayoutDirective,
         LayoutAlignDirective,
         SpBasicHeaderTitleComponent,
+        MatIconButton,
+        MatMenuTrigger,
+        MatMenu,
+        MatMenuItem,
+        MatIcon,
         MatStepper,
         MatStep,
         MatStepLabel,
@@ -69,6 +83,7 @@ export class AdapterConfigurationComponent implements OnInit, 
OnDestroy {
     private router = inject(Router);
     private translate = inject(TranslateService);
     private stateService = inject(AdapterConfigurationStateService);
+    private dialogService = inject(DialogService);
 
     @Input() adapterDescription: AdapterDescription;
 
@@ -82,6 +97,7 @@ export class AdapterConfigurationComponent implements OnInit, 
OnDestroy {
 
     myStepper: MatStepper;
     pageTitle = '';
+    private pendingManageAdapterResult?: 
ObjectManageDialogResult<AdapterDescription>;
 
     ngOnInit() {
         this.pageTitle = this.isEditMode
@@ -116,6 +132,111 @@ export class AdapterConfigurationComponent implements 
OnInit, OnDestroy {
         this.router.navigate(['connect']).then();
     }
 
+    manageAdapter(): void {
+        const currentAdapter =
+            this.state().adapterDescription ?? this.adapterDescription;
+        const pendingManageResult = this.pendingManageAdapterResult;
+
+        if (!currentAdapter) {
+            return;
+        }
+
+        const resourceConfig: 
ObjectManageDialogResourceConfig<AdapterDescription> =
+            {
+                resourceLabel: 'Adapter',
+                nameLabel: 'Adapter name',
+                descriptionLabel: 'Adapter description',
+                nameProperty: 'name',
+                assetLinkType: 'adapter',
+                assetLinkCheckboxLabel:
+                    'Add the current adapter to an existing asset',
+            };
+
+        const dialogRef = this.dialogService.open(ObjectManageDialogComponent, 
{
+            panelType: PanelType.SLIDE_IN_PANEL,
+            title: this.translate.instant('Manage'),
+            width: '50vw',
+            data: {
+                objectInstanceId:
+                    currentAdapter.correspondingDataStreamElementId,
+                resource: { ...currentAdapter },
+                saveMode: 'deferred',
+                resourceConfig,
+                selectedAssets: pendingManageResult?.selectedAssets ?? [],
+                deselectedAssets: pendingManageResult?.deselectedAssets ?? [],
+                originalAssets: pendingManageResult?.originalAssets ?? [],
+                addToAssets: pendingManageResult?.addToAssets ?? true,
+                headerTitle:
+                    this.translate.instant('Manage Adapter ') +
+                    (currentAdapter.name ?? ''),
+            },
+        });
+
+        dialogRef.afterClosed().subscribe(result => {
+            if (result && typeof result !== 'boolean') {
+                this.pendingManageAdapterResult = result;
+                Object.assign(
+                    currentAdapter,
+                    result.resource as AdapterDescription,
+                );
+                this.adapterDescription = currentAdapter;
+                this.displayName = currentAdapter.name;
+                this.pageTitle =
+                    this.translate.instant('Edit adapter: ') + 
this.displayName;
+            }
+        });
+    }
+
+    deleteAdapter(): void {
+        const currentAdapter =
+            this.state().adapterDescription ?? this.adapterDescription;
+
+        if (!currentAdapter) {
+            return;
+        }
+
+        const dialogRef = this.dialogService.open(
+            DeleteAdapterDialogComponent,
+            {
+                panelType: PanelType.STANDARD_PANEL,
+                title: this.translate.instant('Delete Adapter'),
+                width: '70vw',
+                data: {
+                    adapter: currentAdapter,
+                },
+            },
+        );
+
+        dialogRef.afterClosed().subscribe(refresh => {
+            if (refresh) {
+                this.stateService.reset();
+                this.router.navigate(['connect'], {
+                    state: { omitConfirm: true },
+                });
+            }
+        });
+    }
+
+    get pendingPermission() {
+        return this.pendingManageAdapterResult?.permission;
+    }
+
+    get pendingSelectedAssets() {
+        return this.pendingManageAdapterResult?.selectedAssets ?? [];
+    }
+
+    get pendingDeselectedAssets() {
+        return this.pendingManageAdapterResult?.deselectedAssets ?? [];
+    }
+
+    get pendingOriginalAssets() {
+        return this.pendingManageAdapterResult?.originalAssets ?? [];
+    }
+
+    get shouldAddToAssets() {
+        return this.pendingManageAdapterResult?.addToAssets ?? true;
+    }
+
     ngOnDestroy() {
         this.stateService.reset();
     }
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 f12efdc35e..abaea8d027 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
@@ -17,83 +17,89 @@
 -->
 
 <div [formGroup]="startAdapterForm" fxLayout="column" fxFlex="100">
-    <sp-basic-inner-panel
-        [panelTitle]="'Adapter settings' | translate"
-        outerMargin="20px 0px 10px 0px"
-    >
-        <div fxFlex="100">
-            <div fxLayoutAlign="center" fxLayout="column" fxFlex="100">
-                <sp-form-field [level]="2" [label]="'Name' | translate">
-                    <mat-form-field color="accent">
-                        <input
-                            formControlName="adapterName"
-                            matInput
-                            id="input-AdapterName"
-                            [placeholder]="'Name' | translate"
-                            data-cy="sp-adapter-name"
-                            required
-                            (blur)="triggerTutorialAdapterNameAssigned()"
-                        />
-                        @if (
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('invalidName')
-                        ) {
-                            <mat-error>
-                                Name can only contain letters, numbers, special
-                                characters (including umlauts), punctuation,
-                                symbols, and spaces.
-                            </mat-error>
-                        }
-                        @if (
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('leadingOrTrailingWhitespace')
-                        ) {
-                            <mat-error>
-                                Name cannot start or end with a space.
-                            </mat-error>
-                        }
-                        @if (
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('required') ||
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('whiteSpaceOnly')
-                        ) {
-                            <mat-error> Adapter name is required. </mat-error>
-                        }
-                        @if (
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('minlength') ||
-                            startAdapterForm
-                                .get('adapterName')
-                                .hasError('maxlength')
-                        ) {
-                            <mat-error>
-                                Adapter name must have between 3 and 40
-                                characters.
-                            </mat-error>
-                        }
-                    </mat-form-field>
-                </sp-form-field>
-                <sp-form-field [level]="2" [label]="'Description' | translate">
-                    <mat-form-field color="accent">
-                        <input
-                            matInput
-                            id="input-AdapterDescription"
-                            [ngModelOptions]="{ standalone: true }"
-                            [placeholder]="'Description' | translate"
-                            [(ngModel)]="adapterDescription.description"
-                        />
-                    </mat-form-field>
-                </sp-form-field>
+    @if (!isEditMode) {
+        <sp-basic-inner-panel
+            [panelTitle]="'Adapter settings' | translate"
+            outerMargin="20px 0px 10px 0px"
+        >
+            <div fxFlex="100">
+                <div fxLayoutAlign="center" fxLayout="column" fxFlex="100">
+                    <sp-form-field [level]="2" [label]="'Name' | translate">
+                        <mat-form-field color="accent">
+                            <input
+                                formControlName="adapterName"
+                                matInput
+                                id="input-AdapterName"
+                                [placeholder]="'Name' | translate"
+                                data-cy="sp-adapter-name"
+                                required
+                                (blur)="triggerTutorialAdapterNameAssigned()"
+                            />
+                            @if (
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('invalidName')
+                            ) {
+                                <mat-error>
+                                    Name can only contain letters, numbers,
+                                    special characters (including umlauts),
+                                    punctuation, symbols, and spaces.
+                                </mat-error>
+                            }
+                            @if (
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('leadingOrTrailingWhitespace')
+                            ) {
+                                <mat-error>
+                                    Name cannot start or end with a space.
+                                </mat-error>
+                            }
+                            @if (
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('required') ||
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('whiteSpaceOnly')
+                            ) {
+                                <mat-error>
+                                    Adapter name is required.
+                                </mat-error>
+                            }
+                            @if (
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('minlength') ||
+                                startAdapterForm
+                                    .get('adapterName')
+                                    .hasError('maxlength')
+                            ) {
+                                <mat-error>
+                                    Adapter name must have between 3 and 40
+                                    characters.
+                                </mat-error>
+                            }
+                        </mat-form-field>
+                    </sp-form-field>
+                    <sp-form-field
+                        [level]="2"
+                        [label]="'Description' | translate"
+                    >
+                        <mat-form-field color="accent">
+                            <input
+                                matInput
+                                id="input-AdapterDescription"
+                                [ngModelOptions]="{ standalone: true }"
+                                [placeholder]="'Description' | translate"
+                                [(ngModel)]="adapterDescription.description"
+                            />
+                        </mat-form-field>
+                    </sp-form-field>
+                </div>
             </div>
-        </div>
-    </sp-basic-inner-panel>
-
+        </sp-basic-inner-panel>
+    }
     <div fxFlex="100" fxLayout="column">
         @if (!isEditMode) {
             <sp-adapter-options-panel
@@ -107,7 +113,7 @@
             </sp-adapter-options-panel>
         }
 
-        @if (isAssetAdmin) {
+        @if (isAssetAdmin && !isEditMode) {
             <sp-adapter-options-panel
                 [optionTitle]="'Add to Asset' | translate"
                 [optionDescription]="
@@ -282,25 +288,6 @@
                 </sp-adapter-options-panel>
             }
         }
-
-        <sp-adapter-options-panel
-            [optionTitle]="'Show code' | translate"
-            [optionDescription]="
-                'Show code to programmatically deploy this adapter over the 
API'
-                    | translate
-            "
-            optionIcon="code"
-            dataCy="show-code-checkbox"
-            (optionSelectedEmitter)="showCode = $event"
-        >
-            @if (showCode) {
-                <sp-adapter-code-panel
-                    [adapterDescription]="adapterDescription"
-                    class="w-100"
-                >
-                </sp-adapter-code-panel>
-            }
-        </sp-adapter-options-panel>
     </div>
 
     <div fxLayoutAlign="end" style="margin-top: 10px">
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 24410d2e15..32569e903e 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
@@ -26,6 +26,7 @@ import {
 import {
     AdapterDescription,
     EventSchema,
+    Permission,
     ReduceEventRateRule,
     RemoveDuplicateRule,
     SpAssetTreeNode,
@@ -64,7 +65,6 @@ import { MatInput } from '@angular/material/input';
 import { SpAdapterOptionsPanelComponent } from 
'./adapter-options-panel/adapter-options-panel.component';
 import { MatOption, MatSelect } from '@angular/material/select';
 import { MatTooltip } from '@angular/material/tooltip';
-import { AdapterCodePanelComponent } from 
'../../adapter-code-panel/adapter-code-panel.component';
 import { MatButton } from '@angular/material/button';
 
 @Component({
@@ -87,7 +87,6 @@ import { MatButton } from '@angular/material/button';
         MatSelect,
         MatOption,
         MatTooltip,
-        AdapterCodePanelComponent,
         MatButton,
         TranslatePipe,
         TimestampPipe,
@@ -109,6 +108,8 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
     @Input() eventSchema: EventSchema;
 
     @Input() isEditMode: boolean;
+    @Input() permission?: Permission;
+    @Input() addToAssets = true;
 
     /**
      * Cancels the adapter configuration process
@@ -146,9 +147,9 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
     startAdapterNow = true;
     showCode = false;
     showAsset = false;
-    selectedAssets = [];
-    deselectedAssets = [];
-    originalAssets = [];
+    @Input() selectedAssets: SpAssetTreeNode[] = [];
+    @Input() deselectedAssets: SpAssetTreeNode[] = [];
+    @Input() originalAssets: SpAssetTreeNode[] = [];
 
     isAssetAdmin = false;
     isPipelineAdmin = false;
@@ -202,6 +203,8 @@ export class StartAdapterConfigurationComponent implements 
OnInit {
             data: {
                 adapter: this.adapterDescription,
                 editMode: true,
+                permission: this.permission,
+                addToAssets: this.addToAssets,
                 selectedAssets: this.selectedAssets,
                 deselectedAssets: this.deselectedAssets,
                 originalAssets: this.originalAssets,
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index 935b295329..489ceb39ce 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -252,6 +252,14 @@
                         <mat-icon>visibility</mat-icon>
                         <span>{{ 'Show' | translate }}</span>
                     </button>
+                    <button
+                        mat-menu-item
+                        data-cy="show-adapter-code"
+                        (click)="showCodeDialog(element)"
+                    >
+                        <mat-icon>code</mat-icon>
+                        <span>{{ 'Code' | translate }}</span>
+                    </button>
                     <button
                         mat-menu-item
                         data-cy="edit-adapter"
@@ -262,11 +270,11 @@
                     </button>
                     <button
                         mat-menu-item
-                        data-cy="open-manage-permissions"
-                        (click)="showPermissionsDialog(element)"
+                        data-cy="open-manage-adapter"
+                        (click)="showManageDialog(element)"
                     >
-                        <mat-icon>share</mat-icon>
-                        <span>{{ 'Manage permissions' | translate }}</span>
+                        <mat-icon>settings</mat-icon>
+                        <span>{{ 'Manage' | translate }}</span>
                     </button>
                     <button
                         mat-menu-item
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
index ff39d474de..5808e77d53 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts
@@ -18,6 +18,7 @@
 
 import { Component, inject, OnDestroy, OnInit, ViewChild } from 
'@angular/core';
 import {
+    AdapterDescription,
     AdapterMonitoringService,
     AdapterService,
     AdapterSummaryDto,
@@ -37,7 +38,8 @@ import {
     CurrentUserService,
     DialogRef,
     DialogService,
-    ObjectPermissionDialogComponent,
+    ObjectManageDialogComponent,
+    ObjectManageDialogResourceConfig,
     PanelType,
     SpAssetBrowserService,
     SpBasicHeaderTitleComponent,
@@ -51,8 +53,10 @@ import {
     SpTableActionsDirective,
     SpTableComponent,
 } from '@streampipes/shared-ui';
+import { AdapterCodeDialogComponent } from 
'../../dialog/adapter-code-dialog/adapter-code-dialog.component';
 import { DeleteAdapterDialogComponent } from 
'../../dialog/delete-adapter-dialog/delete-adapter-dialog.component';
 import { AllAdapterActionsComponent } from 
'../../dialog/start-all-adapters/all-adapter-actions-dialog.component';
+import { AdapterStartedDialog } from 
'../../dialog/adapter-started/adapter-started-dialog.component';
 import { MatSort, MatSortHeader } from '@angular/material/sort';
 import { Router } from '@angular/router';
 import { SpConnectRoutes } from '../../connect.breadcrumb';
@@ -322,30 +326,94 @@ export class ExistingAdaptersComponent implements OnInit, 
OnDestroy {
         }
     }
 
-    showPermissionsDialog(adapter: AdapterSummaryDto): void {
-        const dialogRef = this.dialogService.open(
-            ObjectPermissionDialogComponent,
-            {
-                panelType: PanelType.SLIDE_IN_PANEL,
-                title: this.translate.instant('Manage permissions'),
-                width: '50vw',
+    showManageDialog(adapter: AdapterSummaryDto): void {
+        this.adapterService
+            .getAdapter(adapter.elementId)
+            .subscribe(fullAdapter => {
+                const resourceConfig: 
ObjectManageDialogResourceConfig<AdapterDescription> =
+                    {
+                        resourceLabel: 'Adapter',
+                        nameLabel: 'Adapter name',
+                        descriptionLabel: 'Adapter description',
+                        nameProperty: 'name',
+                        assetLinkType: 'adapter',
+                        assetLinkCheckboxLabel:
+                            'Add the current adapter to an existing asset',
+                        saveResource: resource =>
+                            this.updateManagedAdapter(
+                                fullAdapter.name,
+                                resource,
+                            ),
+                    };
+
+                const dialogRef = this.dialogService.open(
+                    ObjectManageDialogComponent,
+                    {
+                        panelType: PanelType.SLIDE_IN_PANEL,
+                        title: this.translate.instant('Manage'),
+                        width: '50vw',
+                        data: {
+                            objectInstanceId:
+                                fullAdapter.correspondingDataStreamElementId,
+                            resource: { ...fullAdapter },
+                            saveMode: 'immediate',
+                            resourceConfig,
+                            headerTitle:
+                                this.translate.instant('Manage Adapter ') +
+                                adapter.name,
+                        },
+                    },
+                );
+
+                dialogRef.afterClosed().subscribe(refresh => {
+                    if (refresh) {
+                        this.getAdaptersRunning();
+                    }
+                });
+            });
+    }
+
+    private updateManagedAdapter(
+        originalName: string,
+        updatedAdapter: AdapterDescription,
+    ) {
+        if (originalName === updatedAdapter.name) {
+            return this.adapterService.updateAdapter(updatedAdapter);
+        }
+
+        return new Promise<boolean>(resolve => {
+            const dialogRef = this.dialogService.open(AdapterStartedDialog, {
+                panelType: PanelType.STANDARD_PANEL,
+                title: this.translate.instant('Edit adapter'),
+                width: '70vw',
                 data: {
-                    objectInstanceId: adapter.correspondingDataStreamElementId,
-                    headerTitle:
-                        this.translate.instant(
-                            'Manage permissions for adapter ',
-                        ) + adapter.name,
+                    adapter: updatedAdapter,
+                    editMode: true,
+                    addToAssets: false,
                 },
-            },
-        );
+            });
 
-        dialogRef.afterClosed().subscribe(refresh => {
-            if (refresh) {
-                this.getAdaptersRunning();
-            }
+            dialogRef.afterClosed().subscribe(result => {
+                resolve(result === 'Confirm');
+            });
         });
     }
 
+    showCodeDialog(adapter: AdapterSummaryDto): void {
+        this.adapterService
+            .getAdapter(adapter.elementId)
+            .subscribe(fullAdapter => {
+                this.dialogService.open(AdapterCodeDialogComponent, {
+                    panelType: PanelType.SLIDE_IN_PANEL,
+                    title: this.translate.instant('Show as Code'),
+                    width: '50vw',
+                    data: {
+                        adapter: fullAdapter,
+                    },
+                });
+            });
+    }
+
     /**
      * Start edit mode
      * @param adapter
diff --git 
a/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.html
 
b/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.html
new file mode 100644
index 0000000000..8bcce8d4d0
--- /dev/null
+++ 
b/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.html
@@ -0,0 +1,37 @@
+<!--
+~ Licensed to the Apache Software Foundation (ASF) under one or more
+~ contributor license agreements.  See the NOTICE file distributed with
+~ this work for additional information regarding copyright ownership.
+~ The ASF licenses this file to You under the Apache License, Version 2.0
+~ (the "License"); you may not use this file except in compliance with
+~ the License.  You may obtain a copy of the License at
+~
+~    http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+~
+-->
+
+<div class="sp-dialog-container">
+    <div class="sp-dialog-content">
+        <div fxFlex="100" fxLayout="column" class="p-10">
+            @if (adapter) {
+                <sp-adapter-code-panel
+                    [adapterDescription]="adapter"
+                    maxHeight="none"
+                >
+                </sp-adapter-code-panel>
+            }
+        </div>
+    </div>
+    <mat-divider></mat-divider>
+    <div class="sp-dialog-actions">
+        <button mat-button mat-flat-button class="mat-basic" (click)="close()">
+            {{ 'Close' | translate }}
+        </button>
+    </div>
+</div>
diff --git 
a/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.ts
 
b/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.ts
new file mode 100644
index 0000000000..02dc65397e
--- /dev/null
+++ 
b/ui/src/app/connect/dialog/adapter-code-dialog/adapter-code-dialog.component.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Component, Input, inject } from '@angular/core';
+import { AdapterDescription } from '@streampipes/platform-services';
+import { DialogRef } from '@streampipes/shared-ui';
+import { FlexDirective, LayoutDirective } from '@ngbracket/ngx-layout/flex';
+import { AdapterCodePanelComponent } from 
'../../components/adapter-code-panel/adapter-code-panel.component';
+import { MatDivider } from '@angular/material/divider';
+import { MatButton } from '@angular/material/button';
+import { TranslatePipe } from '@ngx-translate/core';
+
+@Component({
+    selector: 'sp-adapter-code-dialog',
+    templateUrl: './adapter-code-dialog.component.html',
+    imports: [
+        FlexDirective,
+        LayoutDirective,
+        AdapterCodePanelComponent,
+        MatDivider,
+        MatButton,
+        TranslatePipe,
+    ],
+})
+export class AdapterCodeDialogComponent {
+    private dialogRef =
+        inject<DialogRef<AdapterCodeDialogComponent>>(DialogRef);
+
+    @Input()
+    adapter: AdapterDescription;
+
+    close(): void {
+        this.dialogRef.close();
+    }
+}
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 8ab08b408f..1ae3beb014 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
@@ -78,14 +78,23 @@
                 {{ 'Keep editing' | translate }}
             </button>
         }
-        <button
-            id="confirm_adapter_started_button"
-            class="mat-basic"
-            mat-flat-button
-            data-cy="close-adapter-started-dialog-button"
-            (click)="onCloseConfirm()"
-        >
-            {{ 'Close' | translate }}
-        </button>
+        @if (
+            !(
+                adapterUpdatePreflight &&
+                editMode &&
+                !adapterInstalled &&
+                !loading
+            )
+        ) {
+            <button
+                id="confirm_adapter_started_button"
+                class="mat-basic"
+                mat-flat-button
+                data-cy="close-adapter-started-dialog-button"
+                (click)="onCloseConfirm()"
+            >
+                {{ 'Close' | translate }}
+            </button>
+        }
     </div>
 </div>
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 79b4fe9992..dc2d7db4ab 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
@@ -35,7 +35,9 @@ import {
     ErrorMessage,
     LinkageData,
     Message,
+    Permission,
     PipelineOperationStatus,
+    PermissionsService,
     PipelineTemplateService,
     PipelineUpdateInfo,
     SpAssetTreeNode,
@@ -84,6 +86,7 @@ export class AdapterStartedDialog implements OnInit {
     private compactPipelineService = inject(CompactPipelineService);
     private assetSaveService = inject(AssetSaveService);
     private dataLakeService = inject(DatalakeRestService);
+    private permissionsService = inject(PermissionsService);
 
     adapterInstalled = false;
 
@@ -100,6 +103,8 @@ export class AdapterStartedDialog implements OnInit {
     @Input() selectedAssets: SpAssetTreeNode[];
     @Input() deselectedAssets: SpAssetTreeNode[];
     @Input() originalAssets: SpAssetTreeNode[];
+    @Input() permission?: Permission;
+    @Input() addToAssets = true;
 
     /**
      * Indicates if a pipeline to store the adapter events should be started
@@ -185,8 +190,9 @@ export class AdapterStartedDialog implements OnInit {
 
         this.loading = true;
         this.adapterService.updateAdapter(this.adapter).subscribe({
-            next: status => {
+            next: async status => {
                 if (status.success) {
+                    await this.persistManageMetadata();
                     this.onAdapterReady(
                         `Adapter ${this.adapter.name} was successfully updated 
and is available in the pipeline editor.`,
                     );
@@ -195,8 +201,6 @@ export class AdapterStartedDialog implements OnInit {
 
                     this.onAdapterFailure(errorLogMessage);
                 }
-
-                this.addToAsset();
             },
             error: error => {
                 this.onAdapterFailure(error.error);
@@ -334,7 +338,9 @@ export class AdapterStartedDialog implements OnInit {
                 linkageData = this.createLinkageData(this.adapter);
             }
 
-            await this.saveAssets(linkageData);
+            if (this.addToAssets) {
+                await this.saveAssets(linkageData);
+            }
 
             this.setSuccessMessage();
         } catch (err) {
@@ -398,18 +404,32 @@ export class AdapterStartedDialog implements OnInit {
     }
 
     private setSuccessMessage(): void {
-        if (this.selectedAssets.length > 0) {
+        if (this.addToAssets && this.selectedAssets.length > 0) {
             this.addToAssetText = this.translateService.instant(
                 'Your Assets were successfully added.',
             );
         }
-        if (this.deselectedAssets && this.deselectedAssets.length > 0) {
+        if (
+            this.addToAssets &&
+            this.deselectedAssets &&
+            this.deselectedAssets.length > 0
+        ) {
             this.deletedFromAssetText = this.translateService.instant(
                 'Your Assets were successfully deleted.',
             );
         }
     }
 
+    private async persistManageMetadata(): Promise<void> {
+        if (this.permission) {
+            await firstValueFrom(
+                this.permissionsService.updatePermission(this.permission),
+            );
+        }
+
+        await this.addToAsset();
+    }
+
     private formatWithAnd(list: string[]): string {
         if (list.length === 1) return list[0];
         const lastItem = list.pop();


Reply via email to