You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2021/05/19 20:26:13 UTC
[incubator-streampipes] branch dev updated: [STREAMPIPES-362] Add
live preview feature to pipeline editor
This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new a14b76f [STREAMPIPES-362] Add live preview feature to pipeline editor
a14b76f is described below
commit a14b76f3046204d0232bc91917706f2c51aa3eec
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Wed May 19 22:25:57 2021 +0200
[STREAMPIPES-362] Add live preview feature to pipeline editor
---
.../backend/StreamPipesResourceConfig.java | 1 +
.../model/preview/PipelinePreviewModel.java | 38 +++++--
.../manager/execution/http/GraphSubmitter.java | 4 +-
.../matching/PipelineVerificationHandler.java | 21 ++--
.../manager/preview/ActivePipelinePreviews.java | 63 +++++++++++
.../manager/preview/PipelinePreview.java | 125 +++++++++++++++++++++
.../rest/impl/PipelineElementPreview.java | 64 +++++++++++
ui/src/app/core-model/gen/streampipes-model.ts | 22 +++-
.../pipeline-assembly.component.html | 18 ++-
.../pipeline-assembly.component.scss | 10 ++
.../pipeline-assembly.component.ts | 10 +-
.../pipeline-element-options.component.html | 7 +-
.../pipeline-element-preview.component.html | 32 ++++++
.../pipeline-element-preview.component.scss} | 27 +++--
.../pipeline-element-preview.component.ts | 68 +++++++++++
.../components/pipeline/pipeline.component.html | 11 +-
.../components/pipeline/pipeline.component.ts | 63 ++++++++---
ui/src/app/editor/editor.component.html | 2 +-
ui/src/app/editor/editor.component.scss | 4 +-
ui/src/app/editor/editor.module.ts | 2 +
ui/src/app/editor/services/editor.service.ts | 26 ++++-
ui/src/scss/sp/pipeline-element-options.scss | 14 ++-
22 files changed, 559 insertions(+), 73 deletions(-)
diff --git a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
index 2261149..e6d9521 100644
--- a/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
+++ b/streampipes-backend/src/main/java/org/apache/streampipes/backend/StreamPipesResourceConfig.java
@@ -77,6 +77,7 @@ public class StreamPipesResourceConfig extends ResourceConfig {
register(PipelineElementFile.class);
register(PipelineElementImportNoUser.class);
register(PipelineElementImport.class);
+ register(PipelineElementPreview.class);
register(PipelineElementRuntimeInfo.class);
register(PipelineMonitoring.class);
register(PipelineNoUserResource.class);
diff --git a/ui/src/app/editor/editor.component.scss b/streampipes-model/src/main/java/org/apache/streampipes/model/preview/PipelinePreviewModel.java
similarity index 52%
copy from ui/src/app/editor/editor.component.scss
copy to streampipes-model/src/main/java/org/apache/streampipes/model/preview/PipelinePreviewModel.java
index 8aeccf2..b63db03 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/preview/PipelinePreviewModel.java
@@ -15,21 +15,35 @@
* limitations under the License.
*
*/
+package org.apache.streampipes.model.preview;
-@import '../../scss/variables';
+import org.apache.streampipes.model.shared.annotation.TsModel;
-.text-color {
- color: $sp-color-accent;
-}
+import java.util.List;
-.page-container {
- border: 0;
-}
+@TsModel
+public class PipelinePreviewModel {
-.border {
- border: 1px solid #cccccc;
-}
+ private String previewId;
+
+ private List<String> supportedPipelineElementDomIds;
+
+ public PipelinePreviewModel() {
+ }
+
+ public String getPreviewId() {
+ return previewId;
+ }
+
+ public void setPreviewId(String previewId) {
+ this.previewId = previewId;
+ }
+
+ public List<String> getSupportedPipelineElementDomIds() {
+ return supportedPipelineElementDomIds;
+ }
-.icon-stand-margin {
- margin-bottom: 10px;
+ public void setSupportedPipelineElementDomIds(List<String> supportedPipelineElementDomIds) {
+ this.supportedPipelineElementDomIds = supportedPipelineElementDomIds;
+ }
}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/GraphSubmitter.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/GraphSubmitter.java
index 1899ee1..82919a6 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/GraphSubmitter.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/GraphSubmitter.java
@@ -38,7 +38,9 @@ public class GraphSubmitter {
private final static Logger LOG = LoggerFactory.getLogger(GraphSubmitter.class);
- public GraphSubmitter(String pipelineId, String pipelineName, List<InvocableStreamPipesEntity> graphs,
+ public GraphSubmitter(String pipelineId,
+ String pipelineName,
+ List<InvocableStreamPipesEntity> graphs,
List<SpDataSet> dataSets) {
this.graphs = graphs;
this.pipelineId = pipelineId;
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java
index 210c8c4..c6a2bb2 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java
@@ -48,8 +48,13 @@ public class PipelineVerificationHandler {
private final InvocableStreamPipesEntity rootPipelineElement;
public PipelineVerificationHandler(Pipeline pipeline) throws NoSepaInPipelineException {
+ this(pipeline, PipelineVerificationUtils.getRootNode(pipeline));
+ }
+
+ public PipelineVerificationHandler(Pipeline pipeline,
+ InvocableStreamPipesEntity rootNode) {
this.pipeline = pipeline;
- this.rootPipelineElement = PipelineVerificationUtils.getRootNode(pipeline);
+ this.rootPipelineElement = rootNode;
this.invocationGraphs = makeInvocationGraphs();
this.pipelineModificationMessage = new PipelineModificationMessage();
}
@@ -116,12 +121,12 @@ public class PipelineVerificationHandler {
private void updateStaticProperties(List<SpDataStream> inputStreams,
List<StaticProperty> staticProperties) {
staticProperties
- .stream()
- .filter(sp -> (sp instanceof CollectionStaticProperty
- || sp instanceof MappingProperty
- || sp instanceof StaticPropertyGroup
- || sp instanceof StaticPropertyAlternatives))
- .forEach(property -> updateStaticProperty(inputStreams, property));
+ .stream()
+ .filter(sp -> (sp instanceof CollectionStaticProperty
+ || sp instanceof MappingProperty
+ || sp instanceof StaticPropertyGroup
+ || sp instanceof StaticPropertyAlternatives))
+ .forEach(property -> updateStaticProperty(inputStreams, property));
}
private void updateStaticProperty(List<SpDataStream> inputStreams, StaticProperty property) {
@@ -203,4 +208,4 @@ public class PipelineVerificationHandler {
.allMatch(pe -> pe instanceof SpDataStream);
}
-}
\ No newline at end of file
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/ActivePipelinePreviews.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/ActivePipelinePreviews.java
new file mode 100644
index 0000000..7d46e7c
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/ActivePipelinePreviews.java
@@ -0,0 +1,63 @@
+/*
+ * 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.preview;
+
+import org.apache.streampipes.model.base.NamedStreamPipesEntity;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public enum ActivePipelinePreviews {
+
+ INSTANCE;
+
+ private Map<String, List<NamedStreamPipesEntity>> activePreviews;
+
+ ActivePipelinePreviews() {
+ this.activePreviews = new HashMap<>();
+ }
+
+ public void addActivePreview(String previewId,
+ List<NamedStreamPipesEntity> activePreviews) {
+ this.activePreviews.put(previewId, activePreviews);
+ }
+
+ public void removePreview(String previewId) {
+ this.activePreviews.remove(previewId);
+ }
+
+ public List<NamedStreamPipesEntity> getInvocationGraphs(String previewId) {
+ return this.activePreviews.get(previewId);
+ }
+
+ public Optional<NamedStreamPipesEntity> getInvocationGraphForPipelineELement(String previewId,
+ String pipelineElementDomId) {
+ List<NamedStreamPipesEntity> graphs = this.activePreviews.get(previewId);
+
+ if (graphs == null || graphs.size() == 0) {
+ return Optional.empty();
+ } else {
+ return graphs
+ .stream()
+ .filter(g -> g.getDOM().equals(pipelineElementDomId))
+ .findFirst();
+ }
+ }
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/PipelinePreview.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/PipelinePreview.java
new file mode 100644
index 0000000..ddc7bd3
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/preview/PipelinePreview.java
@@ -0,0 +1,125 @@
+/*
+ * 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.preview;
+
+import org.apache.streampipes.manager.data.PipelineGraph;
+import org.apache.streampipes.manager.data.PipelineGraphBuilder;
+import org.apache.streampipes.manager.execution.http.HttpRequestBuilder;
+import org.apache.streampipes.manager.matching.InvocationGraphBuilder;
+import org.apache.streampipes.manager.operations.Operations;
+import org.apache.streampipes.model.SpDataSet;
+import org.apache.streampipes.model.SpDataStream;
+import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
+import org.apache.streampipes.model.base.NamedStreamPipesEntity;
+import org.apache.streampipes.model.graph.DataProcessorInvocation;
+import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.model.preview.PipelinePreviewModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public class PipelinePreview {
+
+ public PipelinePreviewModel initiatePreview(Pipeline pipeline) {
+ String previewId = generatePreviewId();
+ pipeline.setActions(new ArrayList<>());
+ PipelineGraph pipelineGraph = new PipelineGraphBuilder(pipeline).buildGraph();
+ List<NamedStreamPipesEntity> graphs = new ArrayList<>(new InvocationGraphBuilder(pipelineGraph, previewId).buildGraphs());
+ graphs.addAll(pipeline.getStreams().stream().filter(stream -> !(stream instanceof SpDataSet)).collect(Collectors.toList()));
+
+ invokeGraphs(filter(graphs));
+ storeGraphs(previewId, graphs);
+
+ return makePreviewModel(previewId, graphs);
+ }
+
+ public void deletePreview(String previewId) {
+ List<NamedStreamPipesEntity> graphs = ActivePipelinePreviews.INSTANCE.getInvocationGraphs(previewId);
+ detachGraphs(filter(graphs));
+ deleteGraphs(previewId);
+ }
+
+ public String getPipelineElementPreview(String previewId,
+ String pipelineElementDomId) throws IllegalArgumentException {
+ Optional<NamedStreamPipesEntity> graphOpt = ActivePipelinePreviews
+ .INSTANCE
+ .getInvocationGraphForPipelineELement(previewId, pipelineElementDomId);
+
+ if (graphOpt.isPresent()) {
+ NamedStreamPipesEntity graph = graphOpt.get();
+ if (graph instanceof DataProcessorInvocation) {
+ return Operations.getRuntimeInfo(((DataProcessorInvocation) graph).getOutputStream());
+ } else if (graph instanceof SpDataStream) {
+ return Operations.getRuntimeInfo((SpDataStream) graph);
+ } else {
+ throw new IllegalArgumentException("Requested pipeline element is not a data processor");
+ }
+ } else {
+ throw new IllegalArgumentException("Could not find pipeline element");
+ }
+ }
+
+ private void invokeGraphs(List<InvocableStreamPipesEntity> graphs) {
+ graphs.forEach(g -> new HttpRequestBuilder(g, g.getBelongsTo()).invoke());
+ }
+
+ private void detachGraphs(List<InvocableStreamPipesEntity> graphs) {
+ graphs.forEach(g -> new HttpRequestBuilder(g, g.getUri()).detach());
+ }
+
+ private void deleteGraphs(String previewId) {
+ ActivePipelinePreviews.INSTANCE.removePreview(previewId);
+ }
+
+ private void storeGraphs(String previewId,
+ List<NamedStreamPipesEntity> graphs) {
+ ActivePipelinePreviews.INSTANCE.addActivePreview(previewId, graphs);
+ }
+
+ private String generatePreviewId() {
+ return UUID.randomUUID().toString();
+ }
+
+ private PipelinePreviewModel makePreviewModel(String previewId,
+ List<NamedStreamPipesEntity> graphs) {
+ PipelinePreviewModel previewModel = new PipelinePreviewModel();
+ previewModel.setPreviewId(previewId);
+ previewModel.setSupportedPipelineElementDomIds(collectDomIds(graphs));
+
+ return previewModel;
+ }
+
+ private List<String> collectDomIds(List<NamedStreamPipesEntity> graphs) {
+ return graphs
+ .stream()
+ .map(NamedStreamPipesEntity::getDOM)
+ .collect(Collectors.toList());
+ }
+
+ private List<InvocableStreamPipesEntity> filter(List<NamedStreamPipesEntity> graphs) {
+ List<InvocableStreamPipesEntity> dataProcessors = new ArrayList<>();
+ graphs.stream()
+ .filter(g -> g instanceof DataProcessorInvocation)
+ .forEach(p -> dataProcessors.add((DataProcessorInvocation) p));
+
+ return dataProcessors;
+ }
+}
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementPreview.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementPreview.java
new file mode 100644
index 0000000..1b21aa2
--- /dev/null
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementPreview.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rest.impl;
+
+import org.apache.streampipes.manager.preview.PipelinePreview;
+import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.model.preview.PipelinePreviewModel;
+import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("/v2/users/{username}/pipeline-element-preview")
+public class PipelineElementPreview extends AbstractRestResource {
+
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @JacksonSerialized
+ public Response requestPipelinePreview(Pipeline pipeline) {
+ PipelinePreviewModel previewModel = new PipelinePreview().initiatePreview(pipeline);
+
+ return ok(previewModel);
+ }
+
+ @GET
+ @Path("{previewId}/{pipelineElementDomId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response getPipelinePreviewResult(@PathParam("previewId") String previewId,
+ @PathParam("pipelineElementDomId") String pipelineElementDomId) {
+ try {
+ String runtimeInfo = new PipelinePreview().getPipelineElementPreview(previewId, pipelineElementDomId);
+ return ok(runtimeInfo);
+ } catch (IllegalArgumentException e) {
+ return badRequest();
+ }
+ }
+
+ @DELETE
+ @Path("{previewId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public void deletePipelinePreviewRequest(@PathParam("previewId") String previewId) {
+ new PipelinePreview().deletePreview(previewId);
+ }
+
+}
diff --git a/ui/src/app/core-model/gen/streampipes-model.ts b/ui/src/app/core-model/gen/streampipes-model.ts
index 084b63e..aa51aee 100644
--- a/ui/src/app/core-model/gen/streampipes-model.ts
+++ b/ui/src/app/core-model/gen/streampipes-model.ts
@@ -16,10 +16,11 @@
*
*/
+
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2021-05-14 21:55:06.
+// Generated using typescript-generator version 2.27.744 on 2021-05-19 14:58:04.
export class AbstractStreamPipesEntity {
"@class": "org.apache.streampipes.model.base.AbstractStreamPipesEntity" | "org.apache.streampipes.model.base.NamedStreamPipesEntity" | "org.apache.streampipes.model.connect.adapter.AdapterDescription" | "org.apache.streampipes.model.connect.adapter.AdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.GenericAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.SpecificAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.AdapterStre [...]
@@ -189,9 +190,9 @@ export class AdapterDescription extends NamedStreamPipesEntity {
instance.rules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.rules);
instance.category = __getCopyArrayFn(__identity<string>())(data.category);
instance.createdAt = data.createdAt;
- instance.schemaRules = __getCopyArrayFn(__identity<any>())(data.schemaRules);
instance.valueRules = __getCopyArrayFn(__identity<any>())(data.valueRules);
instance.streamRules = __getCopyArrayFn(__identity<any>())(data.streamRules);
+ instance.schemaRules = __getCopyArrayFn(__identity<any>())(data.schemaRules);
instance.couchDBId = data.couchDBId;
instance._rev = data._rev;
return instance;
@@ -2292,6 +2293,21 @@ export class PipelineOperationStatus {
}
}
+export class PipelinePreviewModel {
+ previewId: string;
+ supportedPipelineElementDomIds: string[];
+
+ static fromData(data: PipelinePreviewModel, target?: PipelinePreviewModel): PipelinePreviewModel {
+ if (!data) {
+ return data;
+ }
+ const instance = target || new PipelinePreviewModel();
+ instance.previewId = data.previewId;
+ instance.supportedPipelineElementDomIds = __getCopyArrayFn(__identity<string>())(data.supportedPipelineElementDomIds);
+ return instance;
+ }
+}
+
export class PipelineStatusMessage {
message: string;
messageType: string;
@@ -2663,8 +2679,8 @@ export class SpDataSet extends SpDataStream {
instance.supportedGrounding = EventGrounding.fromData(data.supportedGrounding);
instance.datasetInvocationId = data.datasetInvocationId;
instance.correspondingPipeline = data.correspondingPipeline;
- instance.actualTopicName = data.actualTopicName;
instance.brokerHostname = data.brokerHostname;
+ instance.actualTopicName = data.actualTopicName;
return instance;
}
}
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 ce6c419..c41b62c 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
@@ -22,8 +22,12 @@
<button mat-button matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
[disabled]="!PipelineValidationService.pipelineValid"
(click)="submit()" type="submit">
- <i class="material-icons">save</i> <span>Save pipeline</span>
+ <div fxLayoutAlign="start center" fxLayout="row">
+ <i class="material-icons">save</i>
+ <span> Save pipeline</span>
+ </div>
</button>
+ <span class="assembly-options-divider"></span>
<!-- TODO: Use this once copying of elements is supported -->
<!-- <button mat-button mat-icon-button matTooltip="Pan" [matTooltipPosition]="'above'"-->
<!-- [disabled]="!selectMode"-->
@@ -35,6 +39,15 @@
<!-- (click)="toggleSelectMode()">-->
<!-- <i class="material-icons">mode_edit</i>-->
<!-- </button>-->
+ <button mat-button matTooltip="Data Preview" [matTooltipPosition]="'above'"
+ (click)="triggerPipelinePreview()">
+ <div fxLayoutAlign="start center" fxLayout="row">
+ <i class="material-icons">visibility</i>
+ <span *ngIf="!pipelineComponent.previewModeActive"> Enable live preview</span>
+ <span *ngIf="pipelineComponent.previewModeActive"> Disable live preview</span>
+ </div>
+ </button>
+ <span class="assembly-options-divider"></span>
<button mat-button mat-icon-button matTooltip="Auto Layout" [matTooltipPosition]="'above'"
(click)="autoLayout()">
<i class="material-icons">settings_overscan</i>
@@ -125,7 +138,8 @@
</div>
</div>
<div id="assembly" class="canvas" #assembly>
- <pipeline [pipelineValid]="pipelineValid"
+ <pipeline #pipelineComponent
+ [pipelineValid]="pipelineValid"
[canvasId]="'assembly'"
[rawPipelineModel]="rawPipelineModel"
[allElements]="allElements"
diff --git a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
index 7629335..b84c217 100644
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.scss
@@ -65,7 +65,9 @@
}
.pipeline-assembly-options {
+ padding-left: 5px;
color: white;
+ //background: #f6f6f6;
}
.jtk-surface-nopan {
@@ -147,3 +149,11 @@
left: 28px;
top: 30px;
}
+
+.assembly-options-divider {
+ width: 2px;
+ height: 70%;
+ margin-left: 10px;
+ margin-right: 10px;
+ background: #b8b8b8;
+}
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 385d5c3..d4a0f68 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
@@ -44,6 +44,7 @@ import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scr
import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
import Panzoom, {PanzoomObject} from "@panzoom/panzoom";
import {PipelineElementDraggedService} from "../../services/pipeline-element-dragged.service";
+import {PipelineComponent} from "../pipeline/pipeline.component";
@Component({
@@ -86,6 +87,10 @@ export class PipelineAssemblyComponent implements OnInit {
config: any = {};
@ViewChild("outerCanvas") pipelineCanvas: ElementRef;
+
+ @ViewChild("pipelineComponent")
+ pipelineComponent: PipelineComponent;
+
panzoom: PanzoomObject;
constructor(private JsPlumbFactoryService: JsplumbFactoryService,
@@ -279,7 +284,6 @@ export class PipelineAssemblyComponent implements OnInit {
}
panRight() {
- console.log("panning right");
this.pan(-100, 0);
}
@@ -306,4 +310,8 @@ export class PipelineAssemblyComponent implements OnInit {
this.panzoom.pan(x, y);
}
+ triggerPipelinePreview() {
+ this.pipelineComponent.initiatePipelineElementPreview();
+ }
+
}
diff --git a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html
index 976a382..8b022fb 100644
--- a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html
+++ b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html
@@ -42,7 +42,7 @@
<button mat-button mat-icon-button matTooltip="Compatible Elements" [matTooltipPosition]="'below'"
[disabled]="!possibleElements || possibleElements.length == 0 || !isConfigured()"
(click)="openPossibleElementsDialog()">
- <i class="material-icons">visibility</i>
+ <i class="material-icons">account_tree</i>
</button>
</span>
<span class="options-button recommended-button"
@@ -56,7 +56,8 @@
</span>
<span class="options-button help-button" style="z-index:10">
<button matTooltip="Help" [matTooltipPosition]="'below'"
- mat-button mat-icon-button (click)="openHelpDialog()">?
+ mat-button mat-icon-button (click)="openHelpDialog()">
+ <i class="material-icons">help</i>
</button>
</span>
<div class="editor-pe-info" [ngClass]="'pe-info-' + pipelineElementCssType">
@@ -67,4 +68,4 @@
[pipelineElementDomId]="pipelineElement.payload.dom"
[recommendedElements]="recommendedElements"
[recommendationsShown]="recommendationsShown" *ngIf="recommendationsAvailable"></pipeline-element-recommendation>
-</div>
\ No newline at end of file
+</div>
diff --git a/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.html b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.html
new file mode 100644
index 0000000..7a9c5ab
--- /dev/null
+++ b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.html
@@ -0,0 +1,32 @@
+<!--
+ ~ 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="data-preview">
+ <div *ngIf="!runtimeData" fxFlex="100" fxLayout="column" fxLayoutAlign="center center">
+ <mat-spinner [diameter]="20"></mat-spinner>
+ <span class="preview-table mt-10">Waiting for live data...</span>
+ </div>
+ <table class="dataTable row-border hover preview-table" *ngIf="runtimeData">
+ <tbody id="preview-data-rows-id">
+ <tr *ngFor="let item of runtimeData | keyvalue">
+ <td>{{item.key}}</td>
+ <td><b>{{item.value}}</b></td>
+ </tr>
+ </tbody>
+ </table>
+</div>
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.scss
similarity index 73%
copy from ui/src/app/editor/editor.component.scss
copy to ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.scss
index 8aeccf2..c201870 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.scss
@@ -16,20 +16,23 @@
*
*/
-@import '../../scss/variables';
-
-.text-color {
- color: $sp-color-accent;
-}
-
-.page-container {
- border: 0;
+.data-preview {
+ position: relative;
+ left: 0px;
+ top: 120px;
+ width: 250px;
+ height: 120px;
+ background: white;
+ overflow: auto;
+ border: 1px solid gray;
+ box-shadow: 0.175em 0.175em 0 0 rgba(15, 28, 63, 0.125);
+ z-index:50;
}
-.border {
- border: 1px solid #cccccc;
+.preview-table {
+ font-size:9pt;
}
-.icon-stand-margin {
- margin-bottom: 10px;
+.mt-10 {
+ margin-top: 10px;
}
diff --git a/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.ts b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.ts
new file mode 100644
index 0000000..1d68604
--- /dev/null
+++ b/ui/src/app/editor/components/pipeline-element-preview/pipeline-element-preview.component.ts
@@ -0,0 +1,68 @@
+/*
+ * 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, OnDestroy, OnInit} from "@angular/core";
+import {EditorService} from "../../services/editor.service";
+
+@Component({
+ selector: 'pipeline-element-preview',
+ templateUrl: './pipeline-element-preview.component.html',
+ styleUrls: ['./pipeline-element-preview.component.scss']
+})
+export class PipelineElementPreviewComponent implements OnInit, OnDestroy {
+
+ @Input()
+ previewId: string;
+
+ @Input()
+ pipelineElementDomId: string;
+
+ runtimeData: ReadonlyMap<string, unknown>;
+
+ runtimeDataError: boolean = false;
+ timer: any;
+
+ constructor(private editorService: EditorService) {
+
+ }
+
+ ngOnInit(): void {
+ this.getLatestRuntimeInfo();
+ }
+
+ ngOnDestroy(): void {
+ }
+
+ getLatestRuntimeInfo() {
+ this.editorService.getPipelinePreviewResult(this.previewId, this.pipelineElementDomId).subscribe(data => {
+ if (data) {
+ this.runtimeDataError = false;
+ if (!(Object.keys(data).length === 0 && data.constructor === Object)) {
+ this.runtimeData = data;
+ }
+
+ this.timer = setTimeout(() => {
+ this.getLatestRuntimeInfo();
+ }, 1000);
+ } else {
+ this.runtimeDataError = true;
+ }
+ });
+ }
+
+}
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.html b/ui/src/app/editor/components/pipeline/pipeline.component.html
index b3736fa..603b15e 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.html
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.html
@@ -27,8 +27,10 @@
<div class="pipeline-element-progress-container sp-fade" *ngIf="pipelineElement.settings.loadingStatus">
<mat-spinner [mode]="'indeterminate'" class="pipeline-element-progress" [diameter]="40"></mat-spinner>
</div>
- <div class="pipeline-element-loading-container sp-fade-opacity" *ngIf="pipelineElement.settings.loadingStatus"></div>
- <div class="pipeline-element-configuration-invalid {{pipelineElement.type === 'stream' ? 'pi-stream' : 'pi-processor'}}" *ngIf="!pipelineElement.settings.completed">
+ <div class="pipeline-element-loading-container sp-fade-opacity"
+ *ngIf="pipelineElement.settings.loadingStatus"></div>
+ <div class="pipeline-element-configuration-invalid {{pipelineElement.type === 'stream' ? 'pi-stream' : 'pi-processor'}}"
+ *ngIf="!pipelineElement.settings.completed">
<i class="material-icons pipeline-element-configuration-invalid-icon">
warning
</i>
@@ -45,6 +47,11 @@
[pipelineElementId]="pipelineElement.type == 'stream' ? pipelineElement.payload.elementId : pipelineElement.payload.belongsTo"
[internalId]="pipelineElement.payload.dom">
</pipeline-element-options>
+ <pipeline-element-preview *ngIf="previewModeActive && (pipelinePreview.supportedPipelineElementDomIds.indexOf(pipelineElement.payload.dom) > -1)"
+ [previewId]="pipelinePreview.previewId"
+ [pipelineElementDomId]="pipelineElement.payload.dom">
+
+ </pipeline-element-preview>
</span>
</div>
diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts
index 22ad8c0..e9b8bc7 100644
--- a/ui/src/app/editor/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts
@@ -38,7 +38,7 @@ import {
import {
CustomOutputStrategy,
DataProcessorInvocation, DataSinkInvocation, ErrorMessage,
- Pipeline, SpDataSet,
+ Pipeline, PipelinePreviewModel, SpDataSet,
SpDataStream, SpDataStreamUnion
} from "../../../core-model/gen/streampipes-model";
import {ObjectProvider} from "../../services/object-provider.service";
@@ -104,6 +104,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
JsplumbBridge: JsplumbBridge;
+ previewModeActive: boolean = false;
+ pipelinePreview: PipelinePreviewModel;
+
constructor(private JsplumbService: JsplumbService,
private PipelineEditorService: PipelineEditorService,
private JsplumbFactoryService: JsplumbFactoryService,
@@ -113,7 +116,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
private PipelineValidationService: PipelineValidationService,
private dialogService: DialogService,
private dialog: MatDialog,
- private ngZone: NgZone) {
+ private ngZone: NgZone,) {
this.plumbReady = false;
this.currentMouseOverElement = "";
this.currentPipelineModel = new Pipeline();
@@ -140,6 +143,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
}
ngOnDestroy() {
+ this.deletePipelineElementPreview(false);
this.JsplumbBridge.deleteEveryEndpoint();
this.plumbReady = false;
}
@@ -220,20 +224,19 @@ export class PipelineComponent implements OnInit, OnDestroy {
this.JsplumbService.dataStreamDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as SpDataSet, true, false);
});
}, 0);
- }
- else if (ui.draggable.hasClass('stream')) {
+ } else if (ui.draggable.hasClass('stream')) {
this.checkTopicModel(pipelineElementConfig);
} else if (ui.draggable.hasClass('sepa')) {
- setTimeout(() => {
- this.JsplumbService.dataProcessorDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataProcessorInvocation, true, false);
- }, 10);
+ setTimeout(() => {
+ this.JsplumbService.dataProcessorDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataProcessorInvocation, true, false);
+ }, 10);
} else if (ui.draggable.hasClass('action')) {
- setTimeout(() => {
- this.JsplumbService.dataSinkDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataSinkInvocation, true, false);
- }, 10);
+ setTimeout(() => {
+ this.JsplumbService.dataSinkDropped(pipelineElementConfig.payload.dom, pipelineElementConfig.payload as DataSinkInvocation, true, false);
+ }, 10);
}
if (this.ShepherdService.isTourActive()) {
- this.ShepherdService.trigger("drop-" +pipelineElementConfig.type);
+ this.ShepherdService.trigger("drop-" + pipelineElementConfig.type);
}
}
}
@@ -246,12 +249,12 @@ export class PipelineComponent implements OnInit, OnDestroy {
}
checkTopicModel(pipelineElementConfig: PipelineElementConfig) {
- setTimeout(() => {
- this.JsplumbService.dataStreamDropped(pipelineElementConfig.payload.dom,
- pipelineElementConfig.payload as SpDataStream,
- true,
- false);
- }, 10);
+ setTimeout(() => {
+ this.JsplumbService.dataStreamDropped(pipelineElementConfig.payload.dom,
+ pipelineElementConfig.payload as SpDataStream,
+ true,
+ false);
+ }, 10);
var streamDescription = pipelineElementConfig.payload as SpDataStream;
if (streamDescription
@@ -414,7 +417,7 @@ export class PipelineComponent implements OnInit, OnDestroy {
}
showCustomizeDialog(pipelineElementInfo: Tuple2<Boolean, PipelineElementConfig>) {
- const dialogRef = this.dialogService.open(CustomizeComponent,{
+ const dialogRef = this.dialogService.open(CustomizeComponent, {
panelType: PanelType.SLIDE_IN_PANEL,
title: "Customize " + pipelineElementInfo.b.payload.name,
width: "50vw",
@@ -434,6 +437,9 @@ export class PipelineComponent implements OnInit, OnDestroy {
this.JsplumbBridge.getSourceEndpoint(pipelineElementInfo.b.payload.dom).setType("token");
this.triggerPipelineCacheUpdate();
this.announceConfiguredElement(pipelineElementInfo.b);
+ if (this.previewModeActive) {
+ this.deletePipelineElementPreview(true);
+ }
}
this.validatePipeline();
});
@@ -443,5 +449,26 @@ export class PipelineComponent implements OnInit, OnDestroy {
this.EditorService.announceConfiguredElement(pe.payload.dom);
}
+ initiatePipelineElementPreview() {
+ if (!this.previewModeActive) {
+ let pipeline = this.ObjectProvider.makePipeline(this.rawPipelineModel);
+ this.EditorService.initiatePipelinePreview(pipeline).subscribe(response => {
+ this.pipelinePreview = response;
+ this.previewModeActive = true;
+ });
+ } else {
+ this.deletePipelineElementPreview(false);
+ }
+ }
+ deletePipelineElementPreview(resume: boolean) {
+ if (this.previewModeActive) {
+ this.EditorService.deletePipelinePreviewRequest(this.pipelinePreview.previewId).subscribe(response => {
+ this.previewModeActive = false;
+ if (resume) {
+ this.initiatePipelineElementPreview();
+ }
+ });
+ }
+ }
}
diff --git a/ui/src/app/editor/editor.component.html b/ui/src/app/editor/editor.component.html
index 5d47715..cf750ae 100644
--- a/ui/src/app/editor/editor.component.html
+++ b/ui/src/app/editor/editor.component.html
@@ -42,13 +42,13 @@
<div id="shepherd-test"
style="background-color:#f6f6f6;padding:0px;border-bottom:1px solid #ffffff">
<pipeline-element-icon-stand
- class="icon-stand-margin"
[activeType]="activeType"
[currentElements]="currentElements"
*ngIf="allElementsLoaded && !pipelineCanvasMaximized">
</pipeline-element-icon-stand>
</div>
<pipeline-assembly fxFlex="1"
+ class="pipeline-assembly-margin"
[rawPipelineModel]="rawPipelineModel"
[allElements]="allElements"
[currentModifiedPipelineId]="currentModifiedPipelineId"
diff --git a/ui/src/app/editor/editor.component.scss b/ui/src/app/editor/editor.component.scss
index 8aeccf2..b8cc5df 100644
--- a/ui/src/app/editor/editor.component.scss
+++ b/ui/src/app/editor/editor.component.scss
@@ -30,6 +30,6 @@
border: 1px solid #cccccc;
}
-.icon-stand-margin {
- margin-bottom: 10px;
+.pipeline-assembly-margin {
+ margin-top: 10px;
}
diff --git a/ui/src/app/editor/editor.module.ts b/ui/src/app/editor/editor.module.ts
index b5c10e0..da8f5e0 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -65,6 +65,7 @@ import {PipelineElementDraggedService} from "./services/pipeline-element-dragged
import {PipelineCanvasScrollingService} from "./services/pipeline-canvas-scrolling.service";
import {JsplumbEndpointService} from "./services/jsplumb-endpoint.service";
import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
+import {PipelineElementPreviewComponent} from "./components/pipeline-element-preview/pipeline-element-preview.component";
@NgModule({
imports: [
@@ -99,6 +100,7 @@ import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
PipelineElementDocumentationComponent,
PipelineElementIconStandComponent,
PipelineElementOptionsComponent,
+ PipelineElementPreviewComponent,
PipelineElementRecommendationComponent,
PipelineElementTemplateConfigComponent,
PipelineComponent,
diff --git a/ui/src/app/editor/services/editor.service.ts b/ui/src/app/editor/services/editor.service.ts
index 4ec4b83..18b115d 100644
--- a/ui/src/app/editor/services/editor.service.ts
+++ b/ui/src/app/editor/services/editor.service.ts
@@ -21,9 +21,9 @@ import {HttpClient} from "@angular/common/http";
import {
DataProcessorInvocation,
DataSetModificationMessage,
- DataSinkInvocation,
+ DataSinkInvocation, Pipeline,
PipelineElementRecommendationMessage,
- PipelineModificationMessage,
+ PipelineModificationMessage, PipelinePreviewModel,
SpDataSet,
SpDataStream
} from "../../core-model/gen/streampipes-model";
@@ -141,4 +141,24 @@ export class EditorService {
}
});
}
-}
\ No newline at end of file
+
+ initiatePipelinePreview(pipeline: Pipeline): Observable<PipelinePreviewModel> {
+ return this.http.post(this.pipelinePreviewBasePath, pipeline)
+ .pipe(map(response => PipelinePreviewModel.fromData(response as any)));
+ }
+
+ deletePipelinePreviewRequest(previewId: string): Observable<any> {
+ return this.http.delete(this.pipelinePreviewBasePath + "/" + previewId);
+ }
+
+ getPipelinePreviewResult(previewId: string, pipelineElementDomId: string): Observable<any> {
+ return this.http.get(this.pipelinePreviewBasePath
+ + "/"
+ + previewId
+ + "/" + pipelineElementDomId, {headers: { ignoreLoadingBar: '' }});
+ }
+
+ get pipelinePreviewBasePath() {
+ return this.platformServicesCommons.authUserBasePath() + "/pipeline-element-preview";
+ }
+}
diff --git a/ui/src/scss/sp/pipeline-element-options.scss b/ui/src/scss/sp/pipeline-element-options.scss
index e63c5dc..a61d505 100644
--- a/ui/src/scss/sp/pipeline-element-options.scss
+++ b/ui/src/scss/sp/pipeline-element-options.scss
@@ -25,20 +25,24 @@
width: 40%;
border-radius: 50%;
box-shadow: 0 0 3px $sp-color-accent;
- line-height: 1.6;
text-align: center;
- padding: 2px 0;
+ padding: 1px 1px;
background-color: $sp-color-accent;
color: rgba(255, 255, 255, 0.75);
}
.customize-button {
- left: 5%;
+ left: -20%;
+ top: -10%;
+}
+
+.preview-button {
+ left: 25%;
top: -10%;
}
.delete-button {
- left: 55%;
+ left: 70%;
top: -10%;
background-color: darkred;
box-shadow: 0 0 3px darkred;
@@ -119,4 +123,4 @@
.pe-info-set {
background: $sp-color-set;
color: white;
-}
\ No newline at end of file
+}