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

riemer pushed a commit to branch 
3292-show-adapter-and-pipeline-configuration-in-ui
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit de921e539a69fe88c657b0d60beb9a9f7224c67f
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Oct 9 09:27:38 2024 +0200

    Show pipeline code in UI
---
 .../connect/adapter/compact/CreateOptions.java     |  7 +-
 .../pipeline/compact/CompactPipelineElement.java   |  3 +
 .../compact/CompactPipelineManagement.java         | 14 ++++
 .../generation/CompactPipelineConverter.java       | 85 ++++++++++++++++++++++
 .../PipelineElementConfigurationStep.java          | 12 +--
 .../streampipes/rest/impl/PipelineResource.java    | 22 ++++++
 .../src/lib/apis/pipeline.service.ts               |  8 ++
 .../adapter-code-panel.component.html              | 21 ++----
 ui/src/app/connect/connect.module.ts               |  4 -
 .../configuration-code-panel.component.html}       | 34 ++++-----
 .../configuration-code-panel.component.scss        | 17 +++--
 .../configuration-code-panel.component.ts}         | 20 ++---
 ui/src/app/core-ui/core-ui.module.ts               |  9 +++
 .../pipes}/json-pretty-print.pipe.ts               |  0
 .../pipes}/yaml-pretty-print.pipe.ts               |  0
 .../save-pipeline-settings.component.html          | 22 ++++--
 .../save-pipeline-settings.component.scss          |  4 +
 .../save-pipeline-settings.component.ts            | 16 +++-
 .../pipeline-details-toolbar.component.html        | 12 +++
 .../pipeline-details-toolbar.component.ts          |  3 +
 .../pipeline-code-dialog.component.html}           | 38 +++++-----
 .../pipeline-code-dialog.component.ts}             | 37 ++++++----
 .../pipeline-details.component.html                |  1 +
 .../pipeline-details/pipeline-details.component.ts | 15 ++++
 .../pipeline-details/pipeline-details.module.ts    |  2 +
 25 files changed, 308 insertions(+), 98 deletions(-)

diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
index 6fc266ff21..ee8ea06f61 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
@@ -18,6 +18,9 @@
 
 package org.apache.streampipes.model.connect.adapter.compact;
 
-public record CreateOptions(boolean persist,
-                            boolean start) {
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public record CreateOptions(Boolean persist,
+                            Boolean start) {
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/compact/CompactPipelineElement.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/compact/CompactPipelineElement.java
index 0f976c8145..b145c2aad3 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/compact/CompactPipelineElement.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/compact/CompactPipelineElement.java
@@ -18,9 +18,12 @@
 
 package org.apache.streampipes.model.pipeline.compact;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+
 import java.util.List;
 import java.util.Map;
 
+@JsonInclude(JsonInclude.Include.NON_NULL)
 public record CompactPipelineElement(String type,
                                      String ref,
                                      String id,
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/CompactPipelineManagement.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/CompactPipelineManagement.java
index 1a0a1cd44e..e0137e13b4 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/CompactPipelineManagement.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/CompactPipelineManagement.java
@@ -19,12 +19,16 @@
 package org.apache.streampipes.manager.pipeline.compact;
 
 import org.apache.streampipes.manager.matching.PipelineVerificationHandlerV2;
+import 
org.apache.streampipes.manager.pipeline.compact.generation.CompactPipelineConverter;
 import 
org.apache.streampipes.manager.pipeline.compact.generation.PipelineElementConfigurationStep;
+import org.apache.streampipes.model.connect.adapter.compact.CreateOptions;
 import org.apache.streampipes.model.pipeline.Pipeline;
 import org.apache.streampipes.model.pipeline.PipelineModificationResult;
 import org.apache.streampipes.model.pipeline.compact.CompactPipeline;
 import org.apache.streampipes.storage.api.IPipelineElementDescriptionStorage;
 
+import java.util.UUID;
+
 public class CompactPipelineManagement {
 
   private final IPipelineElementDescriptionStorage storage;
@@ -42,6 +46,16 @@ public class CompactPipelineManagement {
     return new PipelineVerificationHandlerV2(pipeline).makeModifiedPipeline();
   }
 
+  public CompactPipeline convertPipeline(Pipeline pipeline) {
+    return new CompactPipeline(
+        pipeline.getElementId() != null ? pipeline.getElementId() : 
UUID.randomUUID().toString(),
+        pipeline.getName(),
+        pipeline.getDescription(),
+        new CompactPipelineConverter().convert(pipeline),
+        new CreateOptions(null, false)
+    );
+  }
+
   private void applyPipelineBasics(CompactPipeline compactPipeline,
                                    Pipeline pipeline) {
     pipeline.setElementId(compactPipeline.id());
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/CompactPipelineConverter.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/CompactPipelineConverter.java
new file mode 100644
index 0000000000..bebc3b9286
--- /dev/null
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/CompactPipelineConverter.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.streampipes.manager.pipeline.compact.generation;
+
+import org.apache.streampipes.manager.template.CompactConfigGenerator;
+import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.model.pipeline.compact.CompactPipelineElement;
+import org.apache.streampipes.model.staticproperty.StaticProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class CompactPipelineConverter {
+
+  public List<CompactPipelineElement> convert(Pipeline pipeline) {
+    var pipelineElements = new ArrayList<CompactPipelineElement>();
+
+    pipeline.getStreams().stream()
+        .map(stream -> createElement(
+            PipelineElementConfigurationStep.STREAM_TYPE,
+            stream.getDom(),
+            stream.getElementId(),
+            null,
+            null))
+        .forEach(pipelineElements::add);
+
+    pipeline.getSepas().stream()
+        .map(processor -> createElement(
+            PipelineElementConfigurationStep.PROCESSOR_TYPE,
+            processor.getDom(),
+            processor.getAppId(),
+            processor.getConnectedTo(),
+            getConfig(processor.getStaticProperties())))
+        .forEach(pipelineElements::add);
+
+    pipeline.getActions().stream()
+        .map(sink -> createElement(
+            PipelineElementConfigurationStep.SINK_TYPE,
+            sink.getDom(),
+            sink.getAppId(),
+            sink.getConnectedTo(),
+            getConfig(sink.getStaticProperties())))
+        .forEach(pipelineElements::add);
+
+    return pipelineElements;
+  }
+
+  private CompactPipelineElement createElement(String type,
+                                               String ref,
+                                               String elementId,
+                                               List<String> connectedTo,
+                                               List<Map<String, Object>> 
config) {
+    var connections = connectedTo != null ? connectedTo.stream()
+        .map(this::replaceId)
+        .toList() : null;
+    return new CompactPipelineElement(type, replaceId(ref), elementId, 
connections, config);
+  }
+
+  public List<Map<String, Object>> getConfig(List<StaticProperty> 
staticProperties) {
+    var configs = new ArrayList<Map<String, Object>>();
+    staticProperties.forEach(c -> configs.add(new 
CompactConfigGenerator(c).toTemplateValue()));
+    return configs;
+  }
+
+  private String replaceId(String id) {
+    return id.replaceAll(InvocablePipelineElementGenerator.ID_PREFIX, "");
+  }
+}
diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/PipelineElementConfigurationStep.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/PipelineElementConfigurationStep.java
index ec5539414c..ec61f414be 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/PipelineElementConfigurationStep.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/pipeline/compact/generation/PipelineElementConfigurationStep.java
@@ -28,9 +28,9 @@ import 
org.apache.streampipes.storage.api.IPipelineElementDescriptionStorage;
 
 public class PipelineElementConfigurationStep implements 
CompactPipelineGenerator {
 
-  private static final String StreamType = "stream";
-  private static final String ProcessorType = "processor";
-  private static final String SinkType = "sink";
+  public static final String STREAM_TYPE = "stream";
+  public static final String PROCESSOR_TYPE = "processor";
+  public static final String SINK_TYPE = "sink";
 
   private final IPipelineElementDescriptionStorage storage;
 
@@ -42,11 +42,11 @@ public class PipelineElementConfigurationStep implements 
CompactPipelineGenerato
   public void apply(Pipeline pipeline,
                     CompactPipeline compactPipeline) throws Exception {
     compactPipeline.pipelineElements().forEach(pe -> {
-      if (pe.type().equalsIgnoreCase(StreamType)) {
+      if (pe.type().equalsIgnoreCase(STREAM_TYPE)) {
         pipeline.getStreams().add(makeStream(pe));
-      } else if (pe.type().equalsIgnoreCase(ProcessorType)) {
+      } else if (pe.type().equalsIgnoreCase(PROCESSOR_TYPE)) {
         pipeline.getSepas().add(makeProcessor(pe));
-      } else if (pe.type().equalsIgnoreCase(SinkType)) {
+      } else if (pe.type().equalsIgnoreCase(SINK_TYPE)) {
         pipeline.getActions().add(makeSink(pe));
       }
     });
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
index aa362aba16..a04ac7b2ca 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineResource.java
@@ -22,6 +22,7 @@ import 
org.apache.streampipes.commons.exceptions.NoSuitableSepasAvailableExcepti
 import org.apache.streampipes.manager.execution.status.PipelineStatusManager;
 import org.apache.streampipes.manager.matching.PipelineVerificationHandlerV2;
 import org.apache.streampipes.manager.pipeline.PipelineManager;
+import 
org.apache.streampipes.manager.pipeline.compact.CompactPipelineManagement;
 import org.apache.streampipes.manager.recommender.ElementRecommender;
 import org.apache.streampipes.manager.storage.PipelineStorageService;
 import org.apache.streampipes.model.message.ErrorMessage;
@@ -34,10 +35,12 @@ import org.apache.streampipes.model.message.SuccessMessage;
 import org.apache.streampipes.model.pipeline.Pipeline;
 import 
org.apache.streampipes.model.pipeline.PipelineElementRecommendationMessage;
 import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
+import org.apache.streampipes.model.pipeline.compact.CompactPipeline;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.rest.shared.exception.SpMessageException;
 import org.apache.streampipes.rest.shared.exception.SpNotificationException;
+import org.apache.streampipes.storage.management.StorageDispatcher;
 
 import com.google.gson.JsonSyntaxException;
 import io.swagger.v3.oas.annotations.Hidden;
@@ -48,6 +51,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
@@ -77,6 +81,14 @@ public class PipelineResource extends 
AbstractAuthGuardedRestResource {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(PipelineResource.class);
 
+  private final CompactPipelineManagement compactPipelineManagement;
+
+  public PipelineResource() {
+    this.compactPipelineManagement = new CompactPipelineManagement(
+        
StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineElementDescriptionStorage()
+    );
+  }
+
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
   @Operation(summary = "Get all pipelines of the current user", tags = 
{"Pipeline"}, responses = {
       @ApiResponse(content = {
@@ -164,6 +176,16 @@ public class PipelineResource extends 
AbstractAuthGuardedRestResource {
     return ok(message);
   }
 
+  @PostMapping(
+      path = "compact",
+      consumes = MediaType.APPLICATION_JSON_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE)
+  @Operation(summary = "Convert a pipeline to the compact model", tags = 
{"Pipeline"})
+  @PreAuthorize(AuthConstants.HAS_WRITE_PIPELINE_PRIVILEGE)
+  public ResponseEntity<CompactPipeline> convertToCompactPipeline(@RequestBody 
Pipeline pipeline) {
+    return ok(compactPipelineManagement.convertPipeline(pipeline));
+  }
+
   @PostMapping(
       path = "/recommend/{recId}",
       consumes = MediaType.APPLICATION_JSON_VALUE,
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/apis/pipeline.service.ts 
b/ui/projects/streampipes/platform-services/src/lib/apis/pipeline.service.ts
index d4e084424c..de5c37517e 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/pipeline.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/pipeline.service.ts
@@ -21,6 +21,7 @@ import { HttpClient } from '@angular/common/http';
 import { PlatformServicesCommons } from './commons.service';
 import { Observable } from 'rxjs';
 import {
+    CompactPipeline,
     Message,
     Pipeline,
     PipelineElementRecommendationMessage,
@@ -75,6 +76,13 @@ export class PipelineService {
             .pipe(map(response => Pipeline.fromData(response as Pipeline)));
     }
 
+    convertToCompactPipeline(pipeline: Pipeline): Observable<CompactPipeline> {
+        return this.http.post<CompactPipeline>(
+            `${this.apiBasePath}/pipelines/compact`,
+            pipeline,
+        );
+    }
+
     storePipeline(pipeline: Pipeline): Observable<Message> {
         return this.http.post(`${this.apiBasePath}/pipelines`, pipeline).pipe(
             map(response => {
diff --git 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
 
b/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
index 52b5d4e5f4..8a05afabd3 100644
--- 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
+++ 
b/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
@@ -17,20 +17,9 @@
   -->
 
 <div fxFlex="100" *ngIf="compactAdapter" class="mt-10">
-    <mat-tab-group color="accent" [mat-stretch-tabs]="false">
-        <mat-tab label="YAML">
-            <pre
-                [innerHTML]="compactAdapter | yamlpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-        <mat-tab label="JSON">
-            <pre
-                [innerHTML]="compactAdapter | jsonpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-    </mat-tab-group>
+    <sp-configuration-code-panel
+        [configuration]="compactAdapter"
+        [maxHeight]="maxHeight"
+    >
+    </sp-configuration-code-panel>
 </div>
diff --git a/ui/src/app/connect/connect.module.ts 
b/ui/src/app/connect/connect.module.ts
index 5d2e8b4756..a244a4b217 100644
--- a/ui/src/app/connect/connect.module.ts
+++ b/ui/src/app/connect/connect.module.ts
@@ -62,7 +62,6 @@ import { EditValueTransformationComponent } from 
'./dialog/edit-event-property/c
 import { SpEpSettingsSectionComponent } from 
'./dialog/edit-event-property/components/ep-settings-section/ep-settings-section.component';
 import { SpAdapterOptionsPanelComponent } from 
'./components/adapter-configuration/start-adapter-configuration/adapter-options-panel/adapter-options-panel.component';
 import { SpAdapterTemplateDialogComponent } from 
'./dialog/adapter-template/adapter-template-dialog.component';
-import { JsonPrettyPrintPipe } from './filter/json-pretty-print.pipe';
 import { MatSnackBarModule } from '@angular/material/snack-bar';
 import { AdapterConfigurationHeaderComponent } from 
'./components/adapter-configuration/adapter-configuration-header/adapter-configuration-header.component';
 import { NewAdapterComponent } from 
'./components/new-adapter/new-adapter.component';
@@ -105,7 +104,6 @@ import { SpAdapterDeploymentSettingsComponent } from 
'./components/adapter-confi
 import { SpAdapterDocumentationDialogComponent } from 
'./dialog/adapter-documentation/adapter-documentation-dialog.component';
 import { AdapterDetailsDataComponent } from 
'./components/adapter-details/adapter-details-data/adapter-details-data.component';
 import { AdapterCodePanelComponent } from 
'./components/adapter-code-panel/adapter-code-panel.component';
-import { YamlPrettyPrintPipe } from './filter/yaml-pretty-print.pipe';
 import { AdapterDetailsCodeComponent } from 
'./components/adapter-details/adapter-details-code/adapter-details-code.component';
 
 @NgModule({
@@ -222,8 +220,6 @@ import { AdapterDetailsCodeComponent } from 
'./components/adapter-details/adapte
         EventSchemaPreviewComponent,
         ExistingAdaptersComponent,
         AdapterFilterPipe,
-        JsonPrettyPrintPipe,
-        YamlPrettyPrintPipe,
         AdapterConfigurationComponent,
         TimestampPipe,
         EditCorrectionValueComponent,
diff --git 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.html
similarity index 57%
copy from 
ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
copy to 
ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.html
index 52b5d4e5f4..083d9291c7 100644
--- 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
+++ 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.html
@@ -16,21 +16,19 @@
   ~
   -->
 
-<div fxFlex="100" *ngIf="compactAdapter" class="mt-10">
-    <mat-tab-group color="accent" [mat-stretch-tabs]="false">
-        <mat-tab label="YAML">
-            <pre
-                [innerHTML]="compactAdapter | yamlpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-        <mat-tab label="JSON">
-            <pre
-                [innerHTML]="compactAdapter | jsonpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-    </mat-tab-group>
-</div>
+<mat-tab-group color="accent" [mat-stretch-tabs]="false">
+    <mat-tab label="YAML">
+        <pre
+            [innerHTML]="configuration | yamlpretty"
+            class="preview-text"
+            [ngStyle]="{ maxHeight: maxHeight }"
+        ></pre>
+    </mat-tab>
+    <mat-tab label="JSON">
+        <pre
+            [innerHTML]="configuration | jsonpretty"
+            class="preview-text"
+            [ngStyle]="{ maxHeight: maxHeight }"
+        ></pre>
+    </mat-tab>
+</mat-tab-group>
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.scss
similarity index 76%
copy from 
streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
copy to 
ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.scss
index 6fc266ff21..c02ba8acc6 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/compact/CreateOptions.java
+++ 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.scss
@@ -1,4 +1,4 @@
-/*
+/*!
  * 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.
@@ -16,8 +16,15 @@
  *
  */
 
-package org.apache.streampipes.model.connect.adapter.compact;
-
-public record CreateOptions(boolean persist,
-                            boolean start) {
+.preview-text {
+    background-color: black;
+    font:
+        9pt Inconsolata,
+        monospace;
+    text-shadow: 0 0 5px #c8c8c8;
+    color: white;
+    padding: 10px;
+    max-width: 100%;
+    overflow-y: scroll;
+    white-space: pre-wrap;
 }
diff --git a/ui/src/app/connect/filter/yaml-pretty-print.pipe.ts 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.ts
similarity index 69%
copy from ui/src/app/connect/filter/yaml-pretty-print.pipe.ts
copy to 
ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.ts
index 88f8b4b459..bf8deb6418 100644
--- a/ui/src/app/connect/filter/yaml-pretty-print.pipe.ts
+++ 
b/ui/src/app/core-ui/configuration-code-panel/configuration-code-panel.component.ts
@@ -16,15 +16,17 @@
  *
  */
 
-import { Injectable, Pipe, PipeTransform } from '@angular/core';
-import { parse, stringify } from 'yaml';
+import { Component, Input, OnInit } from '@angular/core';
 
-@Pipe({
-    name: 'yamlpretty',
+@Component({
+    selector: 'sp-configuration-code-panel',
+    templateUrl: './configuration-code-panel.component.html',
+    styleUrls: ['./configuration-code-panel.component.scss'],
 })
-@Injectable({ providedIn: 'root' })
-export class YamlPrettyPrintPipe implements PipeTransform {
-    transform(obj: any) {
-        return stringify(obj).replace(/ /g, '&nbsp;').replace(/\n/g, '<br/>');
-    }
+export class ConfigurationCodePanelComponent {
+    @Input()
+    configuration: any;
+
+    @Input()
+    maxHeight = '300px';
 }
diff --git a/ui/src/app/core-ui/core-ui.module.ts 
b/ui/src/app/core-ui/core-ui.module.ts
index dc2337b5d1..5a8815dce0 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -116,6 +116,9 @@ import { SingleMarkerMapComponent } from 
'./single-marker-map/single-marker-map.
 import { LeafletModule } from '@asymmetrik/ngx-leaflet';
 import { StaticTreeInputTextEditorComponent } from 
'./static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component';
 import { PipelineElementTemplateConfigItemComponent } from 
'./pipeline-element-template-config/pipeline-element-template-config-item/pipeline-element-template-config-item.component';
+import { ConfigurationCodePanelComponent } from 
'./configuration-code-panel/configuration-code-panel.component';
+import { JsonPrettyPrintPipe } from './pipes/json-pretty-print.pipe';
+import { YamlPrettyPrintPipe } from './pipes/yaml-pretty-print.pipe';
 
 @NgModule({
     imports: [
@@ -168,6 +171,7 @@ import { PipelineElementTemplateConfigItemComponent } from 
'./pipeline-element-t
         LeafletModule,
     ],
     declarations: [
+        ConfigurationCodePanelComponent,
         DataDownloadDialogComponent,
         DateInputComponent,
         DisplayRecommendedPipe,
@@ -220,9 +224,12 @@ import { PipelineElementTemplateConfigItemComponent } from 
'./pipeline-element-t
         StatusIndicatorComponent,
         MultiStepStatusIndicatorComponent,
         PipelineOperationStatusComponent,
+        JsonPrettyPrintPipe,
+        YamlPrettyPrintPipe,
     ],
     providers: [MatDatepickerModule, DisplayRecommendedPipe],
     exports: [
+        ConfigurationCodePanelComponent,
         DataDownloadDialogComponent,
         DateInputComponent,
         PipelineElementTemplateConfigComponent,
@@ -255,6 +262,8 @@ import { PipelineElementTemplateConfigItemComponent } from 
'./pipeline-element-t
         MultiStepStatusIndicatorComponent,
         PipelineOperationStatusComponent,
         SingleMarkerMapComponent,
+        JsonPrettyPrintPipe,
+        YamlPrettyPrintPipe,
     ],
 })
 export class CoreUiModule {}
diff --git a/ui/src/app/connect/filter/json-pretty-print.pipe.ts 
b/ui/src/app/core-ui/pipes/json-pretty-print.pipe.ts
similarity index 100%
rename from ui/src/app/connect/filter/json-pretty-print.pipe.ts
rename to ui/src/app/core-ui/pipes/json-pretty-print.pipe.ts
diff --git a/ui/src/app/connect/filter/yaml-pretty-print.pipe.ts 
b/ui/src/app/core-ui/pipes/yaml-pretty-print.pipe.ts
similarity index 100%
rename from ui/src/app/connect/filter/yaml-pretty-print.pipe.ts
rename to ui/src/app/core-ui/pipes/yaml-pretty-print.pipe.ts
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
index 6ff8ea6a14..18f221cbfc 100644
--- 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
+++ 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.html
@@ -63,17 +63,15 @@
                     (blur)="triggerTutorial()"
                 />
                 <mat-error
-                    >Pipeline name must have between 3 and 40
-                    characters.</mat-error
-                >
+                    >Pipeline name must have between 3 and 40 characters.
+                </mat-error>
             </mat-form-field>
             <mat-form-field fxFlex color="accent">
                 <mat-label>Description</mat-label>
                 <input [formControlName]="'pipelineDescription'" matInput />
                 <mat-error
-                    >Pipeline description must not have more than 80
-                    characters.</mat-error
-                >
+                    >Pipeline description must not have more than 80 
characters.
+                </mat-error>
             </mat-form-field>
         </div>
     </form>
@@ -91,4 +89,16 @@
     >
         Navigate to pipeline overview afterwards
     </mat-checkbox>
+    <div class="mt-10">
+        <mat-expansion-panel class="mat-elevation-z0 border-1">
+            <mat-expansion-panel-header
+                >Show pipeline configuration as 
code</mat-expansion-panel-header
+            >
+            <sp-configuration-code-panel
+                [configuration]="compactPipeline"
+                maxHeight="none"
+            >
+            </sp-configuration-code-panel>
+        </mat-expansion-panel>
+    </div>
 </div>
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.scss
 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.scss
index 13cbc4aacb..e22ed79985 100644
--- 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.scss
+++ 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.scss
@@ -15,3 +15,7 @@
  * limitations under the License.
  *
  */
+
+.border-1 {
+    border: 1px solid var(--color-bg-2);
+}
diff --git 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
index d75b2c1aed..fb0daa47bb 100644
--- 
a/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
+++ 
b/ui/src/app/editor/dialog/save-pipeline/save-pipeline-settings/save-pipeline-settings.component.ts
@@ -23,7 +23,11 @@ import {
     UntypedFormGroup,
     Validators,
 } from '@angular/forms';
-import { Pipeline } from '@streampipes/platform-services';
+import {
+    CompactPipeline,
+    Pipeline,
+    PipelineService,
+} from '@streampipes/platform-services';
 import { PipelineStorageOptions } from '../../../model/editor.model';
 
 @Component({
@@ -44,7 +48,12 @@ export class SavePipelineSettingsComponent implements OnInit 
{
     @Input()
     currentPipelineName: string;
 
-    constructor(private shepherdService: ShepherdService) {}
+    compactPipeline: CompactPipeline;
+
+    constructor(
+        private shepherdService: ShepherdService,
+        private pipelineService: PipelineService,
+    ) {}
 
     ngOnInit() {
         this.submitPipelineForm.addControl(
@@ -73,6 +82,9 @@ export class SavePipelineSettingsComponent implements OnInit {
         ].valueChanges.subscribe(value => {
             this.pipeline.description = value;
         });
+        this.pipelineService
+            .convertToCompactPipeline(this.pipeline)
+            .subscribe(p => (this.compactPipeline = p));
     }
 
     triggerTutorial() {
diff --git 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
 
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
index 4a6192901c..01e230ab4a 100644
--- 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
+++ 
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
@@ -30,6 +30,18 @@
             <span *ngIf="previewModeActive">&nbsp;Disable live preview</span>
         </div>
     </button>
+    <button
+        mat-button
+        color="accent"
+        matTooltip="Pipeline as code"
+        [matTooltipPosition]="'above'"
+        (click)="openCodeDialogEmitter.emit()"
+    >
+        <div fxLayoutAlign="start center" fxLayout="row">
+            <i class="material-icons">code</i>
+            <span>&nbsp;View pipeline as code</span>
+        </div>
+    </button>
     <div fxFlex></div>
     <div fxLayoutAlign="end center">
         <button
diff --git 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
 
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
index 9b8417ecc7..32d8aef9c5 100644
--- 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
+++ 
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
@@ -37,4 +37,7 @@ export class PipelineDetailsToolbarComponent {
 
     @Output()
     togglePreviewEmitter: EventEmitter<void> = new EventEmitter();
+
+    @Output()
+    openCodeDialogEmitter: EventEmitter<void> = new EventEmitter();
 }
diff --git 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
 
b/ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.html
similarity index 57%
copy from 
ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
copy to 
ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.html
index 52b5d4e5f4..2b89fa567e 100644
--- 
a/ui/src/app/connect/components/adapter-code-panel/adapter-code-panel.component.html
+++ 
b/ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.html
@@ -16,21 +16,25 @@
   ~
   -->
 
-<div fxFlex="100" *ngIf="compactAdapter" class="mt-10">
-    <mat-tab-group color="accent" [mat-stretch-tabs]="false">
-        <mat-tab label="YAML">
-            <pre
-                [innerHTML]="compactAdapter | yamlpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-        <mat-tab label="JSON">
-            <pre
-                [innerHTML]="compactAdapter | jsonpretty"
-                class="preview-text"
-                [ngStyle]="{ maxHeight: maxHeight }"
-            ></pre>
-        </mat-tab>
-    </mat-tab-group>
+<div class="sp-dialog-container">
+    <div class="sp-dialog-content">
+        <div fxFlex="100" fxLayout="column" class="p-10">
+            <sp-configuration-code-panel
+                [configuration]="compactPipeline"
+                maxHeight="none"
+            >
+            </sp-configuration-code-panel>
+        </div>
+    </div>
+    <mat-divider></mat-divider>
+    <div class="sp-dialog-actions">
+        <button
+            mat-button
+            mat-raised-button
+            class="mat-basic"
+            (click)="close()"
+        >
+            Close
+        </button>
+    </div>
 </div>
diff --git 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
 
b/ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.ts
similarity index 51%
copy from 
ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
copy to 
ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.ts
index 9b8417ecc7..f7c96f031e 100644
--- 
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
+++ 
b/ui/src/app/pipeline-details/dialogs/pipeline-code/pipeline-code-dialog.component.ts
@@ -16,25 +16,36 @@
  *
  */
 
-import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
+import {
+    CompactPipeline,
+    Pipeline,
+    PipelineService,
+} from '@streampipes/platform-services';
+import { DialogRef } from '@streampipes/shared-ui';
 
 @Component({
-    selector: 'sp-pipeline-details-toolbar',
-    templateUrl: './pipeline-details-toolbar.component.html',
+    selector: 'sp-pipeline-code-dialog',
+    templateUrl: './pipeline-code-dialog.component.html',
 })
-export class PipelineDetailsToolbarComponent {
+export class PipelineCodeDialogComponent implements OnInit {
     @Input()
-    autoRefresh: boolean;
+    pipeline: Pipeline;
 
-    @Input()
-    previewModeActive: boolean;
+    compactPipeline: CompactPipeline;
 
-    @Output()
-    autoRefreshChange = new EventEmitter<boolean>();
+    constructor(
+        private pipelineService: PipelineService,
+        private dialogRef: DialogRef<PipelineCodeDialogComponent>,
+    ) {}
 
-    @Output()
-    reloadMetricsEmitter: EventEmitter<void> = new EventEmitter();
+    ngOnInit() {
+        this.pipelineService
+            .convertToCompactPipeline(this.pipeline)
+            .subscribe(p => (this.compactPipeline = p));
+    }
 
-    @Output()
-    togglePreviewEmitter: EventEmitter<void> = new EventEmitter();
+    close() {
+        this.dialogRef.close();
+    }
 }
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.html 
b/ui/src/app/pipeline-details/pipeline-details.component.html
index 500885bccd..e012942490 100644
--- a/ui/src/app/pipeline-details/pipeline-details.component.html
+++ b/ui/src/app/pipeline-details/pipeline-details.component.html
@@ -29,6 +29,7 @@
             (togglePreviewEmitter)="toggleLivePreview()"
             (autoRefreshChange)="autoRefresh = $event"
             (reloadMetricsEmitter)="triggerReload()"
+            (openCodeDialogEmitter)="openPipelineAsCodeDialog()"
         >
         </sp-pipeline-details-toolbar>
     </div>
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.ts 
b/ui/src/app/pipeline-details/pipeline-details.component.ts
index 97c070b1b9..19ec1c45ac 100644
--- a/ui/src/app/pipeline-details/pipeline-details.component.ts
+++ b/ui/src/app/pipeline-details/pipeline-details.component.ts
@@ -31,6 +31,8 @@ import {
 import { PipelineElementUnion } from '../editor/model/editor.model';
 import {
     CurrentUserService,
+    DialogService,
+    PanelType,
     SpBreadcrumbService,
 } from '@streampipes/shared-ui';
 import { SpPipelineRoutes } from '../pipelines/pipelines.routes';
@@ -40,6 +42,7 @@ import { catchError, filter, switchMap } from 
'rxjs/operators';
 import { PipelinePreviewComponent } from 
'./components/preview/pipeline-preview.component';
 import { HttpContext } from '@angular/common/http';
 import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client';
+import { PipelineCodeDialogComponent } from 
'./dialogs/pipeline-code/pipeline-code-dialog.component';
 
 @Component({
     selector: 'sp-pipeline-details-overview-component',
@@ -75,6 +78,7 @@ export class SpPipelineDetailsComponent implements OnInit, 
OnDestroy {
         private currentUserService: CurrentUserService,
         private breadcrumbService: SpBreadcrumbService,
         private pipelineMonitoringService: PipelineMonitoringService,
+        private dialogService: DialogService,
     ) {}
 
     ngOnInit(): void {
@@ -176,6 +180,17 @@ export class SpPipelineDetailsComponent implements OnInit, 
OnDestroy {
         this.pipelinePreviewComponent?.toggleLivePreview();
     }
 
+    openPipelineAsCodeDialog(): void {
+        this.dialogService.open(PipelineCodeDialogComponent, {
+            panelType: PanelType.SLIDE_IN_PANEL,
+            width: '50vw',
+            title: 'Pipeline code',
+            data: {
+                pipeline: this.pipeline,
+            },
+        });
+    }
+
     ngOnDestroy() {
         this.currentUserSub?.unsubscribe();
         this.autoRefreshSub?.unsubscribe();
diff --git a/ui/src/app/pipeline-details/pipeline-details.module.ts 
b/ui/src/app/pipeline-details/pipeline-details.module.ts
index 8b15a1fca6..f05009a578 100644
--- a/ui/src/app/pipeline-details/pipeline-details.module.ts
+++ b/ui/src/app/pipeline-details/pipeline-details.module.ts
@@ -41,6 +41,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
 import { PipelineDetailsToolbarComponent } from 
'./components/pipeline-details-toolbar/pipeline-details-toolbar.component';
 import { MatSlideToggleModule } from '@angular/material/slide-toggle';
 import { MatDivider } from '@angular/material/divider';
+import { PipelineCodeDialogComponent } from 
'./dialogs/pipeline-code/pipeline-code-dialog.component';
 
 @NgModule({
     imports: [
@@ -64,6 +65,7 @@ import { MatDivider } from '@angular/material/divider';
     ],
     declarations: [
         PipelineActionsComponent,
+        PipelineCodeDialogComponent,
         PipelineElementsRowComponent,
         PipelineLogsDialogComponent,
         PipelineStatusComponent,


Reply via email to