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

JHoelli pushed a commit to branch 
1357-harmonize-resource-creation-workflow-for-pipelines
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit e2bc7bc05b61382c5784c6a77a768a0b65458f2f
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Fri Jul 10 08:17:47 2026 +0000

    Create
---
 .../pipeline-assembly-options.component.html       |  10 +-
 .../pipeline-assembly-options.component.ts         |   7 +-
 .../pipeline-assembly.component.html               |   2 +-
 .../pipeline-assembly.component.ts                 | 137 ++++++++++++++++++---
 4 files changed, 134 insertions(+), 22 deletions(-)

diff --git 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.html
 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.html
index a55604f12d..80cfb53eca 100644
--- 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.html
+++ 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.html
@@ -22,7 +22,7 @@
         [matTooltip]="'Save pipeline' | translate"
         [matTooltipPosition]="'above'"
         [disabled]="!pipelineValidationService.pipelineValid"
-        (click)="savePipelineEmitter.emit()"
+        (click)="savePipelineEmitter.emit(startPipelineAfterStorage)"
         type="submit"
         data-cy="sp-editor-save-pipeline"
     >
@@ -31,6 +31,14 @@
             <span>&nbsp;{{ 'Save' | translate }}</span>
         </div>
     </button>
+    <mat-checkbox
+        [(ngModel)]="startPipelineAfterStorage"
+        color="accent"
+        class="ml-10"
+        data-cy="sp-editor-checkbox-start-immediately"
+    >
+        {{ 'Start pipeline immediately' | translate }}
+    </mat-checkbox>
     <span class="assembly-options-divider"></span>
     <button
         mat-flat-button
diff --git 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.ts
 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.ts
index cdeabf855d..19fda82087 100644
--- 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.ts
+++ 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly-options/pipeline-assembly-options.component.ts
@@ -24,6 +24,7 @@ import {
     ViewChild,
     inject,
 } from '@angular/core';
+import { FormsModule } from '@angular/forms';
 import { JsplumbBridge } from '../../../services/jsplumb-bridge.service';
 import { PipelinePositioningService } from 
'../../../services/pipeline-positioning.service';
 import { PipelineValidationService } from 
'../../../services/pipeline-validation.service';
@@ -51,6 +52,7 @@ import {
     LayoutDirective,
 } from '@ngbracket/ngx-layout/flex';
 import { MatButton, MatIconButton } from '@angular/material/button';
+import { MatCheckbox } from '@angular/material/checkbox';
 import { MatTooltip } from '@angular/material/tooltip';
 import { TranslatePipe } from '@ngx-translate/core';
 
@@ -60,9 +62,11 @@ import { TranslatePipe } from '@ngx-translate/core';
     styleUrls: ['./pipeline-assembly-options.component.scss'],
     imports: [
         FlexDirective,
+        FormsModule,
         LayoutDirective,
         LayoutAlignDirective,
         MatButton,
+        MatCheckbox,
         MatTooltip,
         MatIconButton,
         PipelineAssemblyOptionsPipelineCacheComponent,
@@ -70,6 +74,7 @@ import { TranslatePipe } from '@ngx-translate/core';
     ],
 })
 export class PipelineAssemblyOptionsComponent {
+    startPipelineAfterStorage = true;
     editorService = inject(EditorService);
     pipelineValidationService = inject(PipelineValidationService);
     private pipelinePositioningService = inject(PipelinePositioningService);
@@ -92,7 +97,7 @@ export class PipelineAssemblyOptionsComponent {
     previewModeActive: boolean;
 
     @Output()
-    savePipelineEmitter: EventEmitter<void> = new EventEmitter<void>();
+    savePipelineEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
 
     @Output()
     clearAssemblyEmitter: EventEmitter<void> = new EventEmitter<void>();
diff --git 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
index 3914c4fc89..fcaf7c04e9 100644
--- 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
+++ 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.html
@@ -28,7 +28,7 @@
             [previewModeActive]="previewModeActive"
             (clearAssemblyEmitter)="clearAssembly()"
             (togglePreviewEmitter)="togglePreview()"
-            (savePipelineEmitter)="submit()"
+            (savePipelineEmitter)="submit($event)"
             (displayPipelineTemplateEmitter)="displayPipelineTemplate($event)"
         >
         </sp-pipeline-assembly-options>
diff --git 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
index 780a3a4bce..bf233df637 100644
--- 
a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
+++ 
b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
@@ -35,18 +35,23 @@ import { ObjectProvider } from 
'../../services/object-provider.service';
 import {
     DialogService,
     KeyboardShortcutService,
+    ObjectManageDialogComponent,
+    ObjectManageDialogResourceConfig,
     PanelType,
     ShortcutRegistration,
     SpBasicViewComponent,
 } from '@streampipes/shared-ui';
-import { SavePipelineComponent } from 
'../../dialog/save-pipeline/save-pipeline.component';
 import { EditorService } from '../../services/editor.service';
 import {
+    Message,
     Pipeline,
     PipelineCanvasMetadata,
+    PipelineCanvasMetadataService,
+    PipelineOperationStatus,
+    PipelineService,
 } from '@streampipes/platform-services';
 import { JsplumbFactoryService } from '../../services/jsplumb-factory.service';
-import { forkJoin } from 'rxjs';
+import { firstValueFrom, forkJoin } from 'rxjs';
 import { Router } from '@angular/router';
 import { PipelineAssemblyDrawingAreaComponent } from 
'./pipeline-assembly-drawing-area/pipeline-assembly-drawing-area.component';
 import { PipelineAssemblyOptionsComponent } from 
'./pipeline-assembly-options/pipeline-assembly-options.component';
@@ -76,6 +81,8 @@ export class PipelineAssemblyComponent implements 
AfterViewInit, OnDestroy {
     private jsplumbService = inject(JsplumbService);
     private translateService = inject(TranslateService);
     private shortcutService = inject(KeyboardShortcutService);
+    private pipelineService = inject(PipelineService);
+    private pipelineCanvasService = inject(PipelineCanvasMetadataService);
 
     @Input()
     rawPipelineModel: PipelineElementConfig[];
@@ -146,7 +153,7 @@ export class PipelineAssemblyComponent implements 
AfterViewInit, OnDestroy {
     /**
      * Sends the pipeline to the server
      */
-    submit() {
+    submit(startPipelineAfterStorage = true) {
         const pipelineModel = this.rawPipelineModel;
         const pipeline = this.objectProvider.makePipeline(pipelineModel);
         this.pipelinePositioningService.collectPipelineElementPositions(
@@ -157,27 +164,119 @@ export class PipelineAssemblyComponent implements 
AfterViewInit, OnDestroy {
             pipelineModel,
             this.readonly,
         );
-        const dialogRef = this.dialogService.open(SavePipelineComponent, {
+        if (this.originalPipeline) {
+            pipeline._id = this.originalPipeline._id;
+            pipeline.name = this.originalPipeline.name;
+            pipeline.description = this.originalPipeline.description;
+            pipeline.running = this.originalPipeline.running;
+            pipeline.createdAt = this.originalPipeline.createdAt;
+            pipeline.createdByUser = this.originalPipeline.createdByUser;
+        }
+
+        const resourceConfig: ObjectManageDialogResourceConfig<Pipeline> = {
+            resourceLabel: 'Pipeline',
+            nameLabel: 'Pipeline name',
+            descriptionLabel: 'Description',
+            nameProperty: 'name',
+            assetLinkType: 'pipeline',
+            assetLinkCheckboxLabel:
+                'Add the current pipeline to an existing asset',
+            saveResource: resource =>
+                this.savePipelineResource(resource, startPipelineAfterStorage),
+        };
+        const dialogRef = this.dialogService.open(ObjectManageDialogComponent, 
{
             panelType: PanelType.SLIDE_IN_PANEL,
             title: this.translateService.instant('Save pipeline'),
-            width: '40vw',
+            width: '50vw',
             data: {
-                pipeline: pipeline,
-                originalPipeline: this.originalPipeline,
-                pipelineCanvasMetadata: this.pipelineCanvasMetadata,
+                createMode: !this.originalPipeline,
+                objectInstanceId: this.originalPipeline?._id,
+                resource: JSON.parse(JSON.stringify(pipeline)),
+                saveMode: 'immediate',
+                resourceConfig,
+                headerTitle: this.translateService.instant('Save pipeline'),
             },
         });
-        dialogRef
-            .afterClosed()
-            .subscribe((config: { reload: boolean; pipelineId: string }) => {
-                if (config?.reload) {
-                    this.clearAssembly();
-                    this.rawPipelineModel = [];
-                    setTimeout(() => {
-                        this.router.navigate(['pipelines', 'create']);
-                    });
-                }
-            });
+        dialogRef.afterClosed().subscribe(refresh => {
+            if (refresh) {
+                this.editorService.makePipelineAssemblyEmpty(true);
+                this.editorService.removePipelineFromCache().subscribe();
+                this.router.navigate(['pipelines']);
+            }
+        });
+    }
+
+    private async savePipelineResource(
+        pipeline: Pipeline,
+        startPipelineAfterStorage: boolean,
+    ): Promise<void> {
+        const pipelineId = await this.persistPipeline(pipeline);
+        this.pipelineCanvasMetadata.pipelineId = pipelineId;
+        await firstValueFrom(
+            this.pipelineCanvasService.updatePipelineCanvasMetadata(
+                pipelineId,
+                this.pipelineCanvasMetadata,
+            ),
+        );
+
+        if (startPipelineAfterStorage) {
+            await this.startPipeline(pipelineId);
+        }
+    }
+
+    private async persistPipeline(pipeline: Pipeline): Promise<string> {
+        if (this.originalPipeline) {
+            if (pipeline.running) {
+                await this.stopPipeline(this.originalPipeline._id);
+            }
+
+            const result = await firstValueFrom(
+                this.pipelineService.updatePipeline(pipeline),
+            );
+            this.assertSaveSuccess(result);
+            return this.originalPipeline._id;
+        }
+
+        const result = await firstValueFrom(
+            this.pipelineService.storePipeline(pipeline),
+        );
+        this.assertSaveSuccess(result);
+        const pipelineId = result.notifications?.[1]?.description;
+
+        if (!pipelineId) {
+            throw new Error('The pipeline id was missing after saving.');
+        }
+
+        pipeline._id = pipelineId;
+        return pipelineId;
+    }
+
+    private async stopPipeline(pipelineId: string): Promise<void> {
+        const result = await firstValueFrom(
+            this.pipelineService.stopPipeline(pipelineId),
+        );
+
+        if (!result.success) {
+            throw new Error('Stopping the existing pipeline failed.');
+        }
+    }
+
+    private async startPipeline(pipelineId: string): Promise<void> {
+        const result = await firstValueFrom(
+            this.pipelineService.startPipeline(pipelineId),
+        );
+
+        if (!result.success) {
+            throw new Error('Starting the pipeline failed.');
+        }
+    }
+
+    private assertSaveSuccess(
+        result: Message | PipelineOperationStatus,
+    ): asserts result is Message {
+        if (!result.success) {
+            throw new Error('Saving the pipeline failed.');
+        }
     }
 
     togglePreview(): void {

Reply via email to