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> {{ '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 {
