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 2020/06/07 15:55:09 UTC

[incubator-streampipes] branch STREAMPIPES-145 updated: [STREAMPIPES-145] Add pipeline element options

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

riemer pushed a commit to branch STREAMPIPES-145
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


The following commit(s) were added to refs/heads/STREAMPIPES-145 by this push:
     new b675bf4  [STREAMPIPES-145] Add pipeline element options
b675bf4 is described below

commit b675bf41ef55b9096ce379fdb077c6c8cd44d023
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Sun Jun 7 17:54:52 2020 +0200

    [STREAMPIPES-145] Add pipeline element options
---
 .../PipelineElementRecommendationMessage.java      |   3 +
 .../rest/impl/PipelineWithUserResource.java        |   2 +-
 ui/src/app/core-model/gen/streampipes-model.ts     |  42 +++++-
 .../pipeline-assembly.component.html               |  80 +++++-----
 .../pipeline-element-options.component.css         |   0
 .../pipeline-element-options.component.html        |  69 +++++++++
 .../pipeline-element-options.component.ts          | 163 +++++++++++++++++++++
 .../components/pipeline/pipeline.component.html    |  18 +--
 .../components/pipeline/pipeline.component.ts      |   6 +-
 ui/src/app/editor-v2/editor.module.ts              |   4 +
 ui/src/app/editor-v2/services/jsplumb.service.ts   |   3 +-
 .../pipeline-element-recommendation.service.ts     |   8 +-
 ui/src/app/editor/editor.module.ts                 |   2 +-
 13 files changed, 336 insertions(+), 64 deletions(-)

diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/PipelineElementRecommendationMessage.java b/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/PipelineElementRecommendationMessage.java
index 48eb35f..955187a 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/PipelineElementRecommendationMessage.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/pipeline/PipelineElementRecommendationMessage.java
@@ -18,9 +18,12 @@
 
 package org.apache.streampipes.model.pipeline;
 
+import org.apache.streampipes.model.shared.annotation.TsModel;
+
 import java.util.ArrayList;
 import java.util.List;
 
+@TsModel
 public class PipelineElementRecommendationMessage {
 
 	private List<PipelineElementRecommendation> possibleElements;
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineWithUserResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineWithUserResource.java
index 73fe4d3..995c0e2 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineWithUserResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineWithUserResource.java
@@ -169,7 +169,7 @@ public class PipelineWithUserResource extends AbstractRestInterface implements I
     @Path("/recommend")
     @POST
     @Produces(MediaType.APPLICATION_JSON)
-    @GsonWithIds
+    @JacksonSerialized
     public Response recommend(@PathParam("username") String email, Pipeline pipeline) {
         try {
             return ok(Operations.findRecommendedElements(email, pipeline));
diff --git a/ui/src/app/core-model/gen/streampipes-model.ts b/ui/src/app/core-model/gen/streampipes-model.ts
index c680d6c..5bdaca4 100644
--- a/ui/src/app/core-model/gen/streampipes-model.ts
+++ b/ui/src/app/core-model/gen/streampipes-model.ts
@@ -20,7 +20,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.23.603 on 2020-06-07 10:39:36.
+// Generated using typescript-generator version 2.23.603 on 2020-06-07 14:53:44.
 
 export class AbstractStreamPipesEntity {
     "@class": "org.apache.streampipes.model.base.NamedStreamPipesEntity" | "org.apache.streampipes.model.graph.DataSourceDescription" | "org.apache.streampipes.model.SpDataStream" | "org.apache.streampipes.model.SpDataSet" | "org.apache.streampipes.model.base.InvocableStreamPipesEntity" | "org.apache.streampipes.model.graph.DataProcessorInvocation" | "org.apache.streampipes.model.graph.DataSinkInvocation" | "org.apache.streampipes.model.base.UnnamedStreamPipesEntity" | "org.apache.stream [...]
@@ -446,8 +446,8 @@ export class NamedStreamPipesEntity extends AbstractStreamPipesEntity {
         instance.includedLocales = __getCopyArrayFn(__identity<string>())(data.includedLocales);
         instance.applicationLinks = __getCopyArrayFn(ApplicationLink.fromData)(data.applicationLinks);
         instance.connectedTo = __getCopyArrayFn(__identity<string>())(data.connectedTo);
-        instance.dom = data.dom;
         instance.uri = data.uri;
+        instance.dom = data.dom;
         return instance;
     }
 }
@@ -1310,6 +1310,44 @@ export class Pipeline extends ElementComposition {
     }
 }
 
+export class PipelineElementRecommendation {
+    count: number;
+    description: string;
+    elementId: string;
+    name: string;
+    weight: number;
+
+    static fromData(data: PipelineElementRecommendation, target?: PipelineElementRecommendation): PipelineElementRecommendation {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new PipelineElementRecommendation();
+        instance.elementId = data.elementId;
+        instance.name = data.name;
+        instance.description = data.description;
+        instance.weight = data.weight;
+        instance.count = data.count;
+        return instance;
+    }
+}
+
+export class PipelineElementRecommendationMessage {
+    possibleElements: PipelineElementRecommendation[];
+    recommendedElements: PipelineElementRecommendation[];
+    success: boolean;
+
+    static fromData(data: PipelineElementRecommendationMessage, target?: PipelineElementRecommendationMessage): PipelineElementRecommendationMessage {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new PipelineElementRecommendationMessage();
+        instance.possibleElements = __getCopyArrayFn(PipelineElementRecommendation.fromData)(data.possibleElements);
+        instance.recommendedElements = __getCopyArrayFn(PipelineElementRecommendation.fromData)(data.recommendedElements);
+        instance.success = data.success;
+        return instance;
+    }
+}
+
 export class PipelineModification {
     domId: string;
     elementId: string;
diff --git a/ui/src/app/editor-v2/components/pipeline-assembly/pipeline-assembly.component.html b/ui/src/app/editor-v2/components/pipeline-assembly/pipeline-assembly.component.html
index 97139e6..ae7723e 100644
--- a/ui/src/app/editor-v2/components/pipeline-assembly/pipeline-assembly.component.html
+++ b/ui/src/app/editor-v2/components/pipeline-assembly/pipeline-assembly.component.html
@@ -19,63 +19,54 @@
 <div>
     <div class="assemblyOptions sp-blue-bg">
         <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center">
-            <button mat-button style="display:flex;align-items:center;" class="md-button settings-bar-icon-button"
-                       ng-disabled="!pipelineValid"
-                       (click)="submit()" type="submit">
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Save Pipeline-->
-<!--                </md-tooltip>-->
+            <button mat-button matTooltip="Save Pipeline" [matTooltipPosition]="'above'"
+                    style="display:flex;align-items:center;" class="md-button settings-bar-icon-button"
+                    [disabled]="!pipelineValid"
+                    (click)="submit()" type="submit">
                 <mat-icon>save</mat-icon>&nbsp;Save pipeline
             </button>
-            <button mat-button class="md-icon-button settings-bar-icon-button"
-                       ng-disabled="!selectMode"
-                       (click)="toggleSelectMode()">
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Pan-->
-<!--                </md-tooltip>-->
+            <button mat-button matTooltip="Pan" [matTooltipPosition]="'above'"
+                    class="md-icon-button settings-bar-icon-button"
+                    [disabled]="!selectMode"
+                    (click)="toggleSelectMode()">
                 <i class="material-icons">open_with</i>
             </button>
-            <button mat-button class="md-icon-button settings-bar-icon-button" ng-disabled="selectMode"
-                       (click)="toggleSelectMode()">
+            <button mat-button matTooltip="Select" [matTooltipPosition]="'above'"
+                    class="md-icon-button settings-bar-icon-button" [disabled]="selectMode"
+                    (click)="toggleSelectMode()">
                 <i class="material-icons">mode_edit</i>
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Select-->
-<!--                </md-tooltip>-->
             </button>
-            <button mat-button class="md-icon-button settings-bar-icon-button" ng-disabled="currentZoomLevel == 1"
-                       (click)="zoomIn()">
+            <button mat-button matTooltip="Zoom In" [matTooltipPosition]="'above'"
+                    class="md-icon-button settings-bar-icon-button" [disabled]="currentZoomLevel == 1"
+                    (click)="zoomIn()">
                 <i class="material-icons">zoom_in</i>
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Zoom In-->
-<!--                </md-tooltip>-->
             </button>
-            <button mat-button class="md-icon-button settings-bar-icon-button" ng-disabled="currentZoomLevel == 0.5"
-                       (click)="zoomOut()">
+            <button mat-button matTooltip="Zoom Out" [matTooltipPosition]="'above'"
+                    class="md-icon-button settings-bar-icon-button" [disabled]="currentZoomLevel == 0.5"
+                    (click)="zoomOut()">
                 <i class="material-icons">zoom_out</i>
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Zoom Out-->
-<!--                </md-tooltip>-->
             </button>
-            <button mat-button class="md-icon-button settings-bar-icon-button" (click)="autoLayout()">
+            <button mat-button matTooltip="Auto Layout" [matTooltipPosition]="'below'"
+                    class="md-icon-button settings-bar-icon-button" (click)="autoLayout()">
                 <i class="material-icons">settings_overscan</i>
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Auto Layout-->
-<!--                </md-tooltip>-->
             </button>
             <div class="pipeline-cache-block">
                 <div *ngIf="pipelineCached">All pipeline modifications saved.</div>
-<!--                <div ng-if="pipelineCacheRunning"><md-progress-circular-->
-<!--                        md-mode="indeterminate" class="pipeline-cache-progress"-->
-<!--                        md-diameter="10"></md-progress-circular>&nbsp;Saving pipeline-->
-<!--                    modifications</div>-->
+                <!--                <div ng-if="pipelineCacheRunning"><md-progress-circular-->
+                <!--                        md-mode="indeterminate" class="pipeline-cache-progress"-->
+                <!--                        md-diameter="10"></md-progress-circular>&nbsp;Saving pipeline-->
+                <!--                    modifications</div>-->
             </div>
             <span fxFlex></span>
             <div style="position:relative;">
-                <div *ngIf="!isPipelineAssemblyEmpty()" class="pipeline-validation-summary {{PipelineValidationService.errorMessages.length > 0 ? 'pipeline-validation-summary-error' : ''}}" (click)="toggleErrorMessagesDisplayed()">
+                <div *ngIf="!isPipelineAssemblyEmpty()"
+                     class="pipeline-validation-summary {{PipelineValidationService.errorMessages.length > 0 ? 'pipeline-validation-summary-error' : ''}}"
+                     (click)="toggleErrorMessagesDisplayed()">
                     <div fxLayout="row" fxLayoutAlign="center center">
                         <div fxLayout="row" fxLayoutAlign="start center">
                             <i class="material-icons" *ngIf="PipelineValidationService.errorMessages.length > 0">notifications</i>
-                            <i class="material-icons" *ngIf="PipelineValidationService.errorMessages.length === 0">done</i>
+                            <i class="material-icons"
+                               *ngIf="PipelineValidationService.errorMessages.length === 0">done</i>
                         </div>
                         <div>
                             <span *ngIf="PipelineValidationService.errorMessages.length === 0">Pipeline ok</span>
@@ -83,11 +74,14 @@
                         </div>
                     </div>
                 </div>
-                <div class="editor-error-notifications" *ngIf="PipelineValidationService.errorMessages.length > 0 && errorMessagesDisplayed">
-                    <h5 style="color:darkred"><b>{{PipelineValidationService.errorMessages.length}} {{PipelineValidationService.errorMessages.length == 1 ? "error" : "errors"}} found.</b></h5>
+                <div class="editor-error-notifications"
+                     *ngIf="PipelineValidationService.errorMessages.length > 0 && errorMessagesDisplayed">
+                    <h5 style="color:darkred">
+                        <b>{{PipelineValidationService.errorMessages.length}} {{PipelineValidationService.errorMessages.length == 1 ? "error" : "errors"}}
+                            found.</b></h5>
                     <hr/>
                     <div *ngFor="let errorMessage of PipelineValidationService.errorMessages">
-                            <b>{{errorMessage.title}}</b>
+                        <b>{{errorMessage.title}}</b>
                         <div>{{errorMessage.content}}</div>
                         <hr/>
                     </div>
@@ -95,9 +89,9 @@
             </div>
             <button mat-button class="md-icon-button" ng-click="showClearAssemblyConfirmDialog($event)">
                 <i class="material-icons">clear</i>
-<!--                <md-tooltip md-direction="top">-->
-<!--                    Clear Assembly Area-->
-<!--                </md-tooltip>-->
+                <!--                <md-tooltip md-direction="top">-->
+                <!--                    Clear Assembly Area-->
+                <!--                </md-tooltip>-->
             </button>
         </div>
     </div>
diff --git a/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.css b/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.html b/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.html
new file mode 100644
index 0000000..cc33aa9
--- /dev/null
+++ b/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.html
@@ -0,0 +1,69 @@
+<!--
+  ~ 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 *ngIf="currentMouseOverElement==pipelineElement.payload.dom" class="sp-fade-options">
+    <span class="options-button customize-button" *ngIf="pipelineElement.type!='stream'" style="z-index:10">
+        <button mat-button mat-icon-button matTooltip="Configure Element" [matTooltipPosition]="'above'"
+                (click)="openCustomizeDialog()">
+            <i class="material-icons">settings</i>
+            </button>
+    </span>
+    <span class="options-button customize-button"
+          *ngIf="pipelineElement.type=='stream' && isWildcardTopic()"
+          style="z-index:10">
+        <button mat-button mat-icon-button matTooltip="Configure Element" [matTooltipPosition]="'above'"
+                (click)="openCustomizeStreamDialog()" [disabled]="!isRootElement()">
+            <i class="material-icons">settings</i>
+        </button>
+    </span>
+    <span class="options-button delete-button" style="z-index:10">
+        <button mat-button mat-icon-button matTooltip="Delete Element" [matTooltipPosition]="'above'"
+                (click)="removeElement(pipelineElement)" [disabled]="!isRootElement()">
+            <i class="material-icons">clear</i>
+        </button>
+    </span>
+    <span class="options-button possible-button" *ngIf="pipelineElement.type!='action'" style="z-index:10">
+        <button mat-button mat-icon-button matTooltip="Compatible Elements" [matTooltipPosition]="'below'"
+                [disabled]="!possibleElements || possibleElements.length == 0 || !isConfigured()"
+                (click)="EditorDialogManager.openPossibleElementsDialog(rawPipelineModel, possibleElements, pipelineElement.payload.dom)">
+            <i class="material-icons">visibility</i>
+    </button>
+    </span>
+    <span class="options-button recommended-button"
+          *ngIf="pipelineElement.type!='action' && (recommendationsAvailable) && recommendedElements.length > 0"
+          style="z-index:10">
+        <button mat-button mat-icon-button matTooltip="Recommended Elements" [matTooltipPosition]="'below'"
+                (click)="showRecommendations($event)"
+                [disabled]="!recommendationsAvailable || !isConfigured()">
+            <i class="material-icons">add</i>
+        </button>
+    </span>
+    <span class="options-button help-button" style="z-index:10">
+        <button matTooltip="Help" [matTooltipPosition]="'below'"
+                mat-button mat-icon-button ng-click="openHelpDialog()">?
+            </button>
+    </span>
+    <div class="editor-pe-info" [ngClass]="'pe-info-' + pipelineElementCssType">
+        {{pipelineElement.payload.name}}
+    </div>
+</div>
+<!--<pipeline-element-recommendation raw-pipeline-model="ctrl.rawPipelineModel"-->
+<!--                                 pipeline-element-dom-id="ctrl.pipelineElement.payload.DOM"-->
+<!--                                 recommended-elements="ctrl.recommendedElements"-->
+<!--                                 recommendations-shown="ctrl.recommendationsShown"></pipeline-element-recommendation>-->
+
diff --git a/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.ts b/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.ts
new file mode 100644
index 0000000..0c6ca01
--- /dev/null
+++ b/ui/src/app/editor-v2/components/pipeline-element-options/pipeline-element-options.component.ts
@@ -0,0 +1,163 @@
+/*
+ * 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 * as angular from 'angular';
+import {JsplumbBridge} from "../../services/jsplumb-bridge.service";
+import {JsplumbService} from "../../services/jsplumb.service";
+import {PipelineValidationService} from "../../services/pipeline-validation.service";
+import {RestApi} from "../../../services/rest-api.service";
+import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core";
+import {PipelineElementRecommendationService} from "../../services/pipeline-element-recommendation.service";
+import {ObjectProvider} from "../../services/object-provider.service";
+import {
+  InvocablePipelineElementUnion,
+  PipelineElementConfig, PipelineElementType,
+  PipelineElementUnion
+} from "../../model/editor.model";
+import {SpDataStream, WildcardTopicDefinition} from "../../../core-model/gen/streampipes-model";
+import {PipelineElementTypeUtils} from "../../utils/editor.utils";
+
+@Component({
+  selector: 'pipeline-element-options',
+  templateUrl: './pipeline-element-options.component.html',
+  styleUrls: ['./pipeline-element-options.component.css']
+})
+export class PipelineElementOptionsComponent implements OnInit{
+
+  recommendationsAvailable: any;
+  possibleElements: any;
+  recommendedElements: any;
+  recommendationsShown: any;
+  pipelineElementCssType: string;
+
+  @Input()
+  currentMouseOverElement: string;
+
+  @Input()
+  pipelineElementId: string;
+
+  @Input()
+  pipelineValid: boolean;
+
+  @Input()
+  internalId: string;
+
+  @Input()
+  pipelineElement: PipelineElementConfig;
+
+  @Input()
+  rawPipelineModel: PipelineElementConfig[];
+
+  @Input()
+  allElements: PipelineElementUnion[];
+
+  @Output()
+  delete: EventEmitter<PipelineElementConfig> = new EventEmitter<PipelineElementConfig>();
+
+  constructor(private ObjectProvider: ObjectProvider,
+              private PipelineElementRecommendationService: PipelineElementRecommendationService,
+              //private InitTooltips: InitTooltips,
+              private JsplumbBridge: JsplumbBridge,
+              //private EditorDialogManager: EditorDialogManager,
+              private JsplumbService: JsplumbService,
+              //private TransitionService: TransitionService,
+              private PipelineValidationService: PipelineValidationService,
+              private RestApi: RestApi) {
+    this.recommendationsAvailable = false;
+    this.possibleElements = [];
+    this.recommendedElements = [];
+    this.recommendationsShown = false;
+
+  }
+
+  ngOnInit() {
+    // this.$rootScope.$on("SepaElementConfigured", (event, item) => {
+    //   this.pipelineElement.settings.openCustomize = false;
+    //   this.RestApi.updateCachedPipeline(this.rawPipelineModel);
+    //   if (item === this.pipelineElement.payload.DOM) {
+    //     this.initRecs(this.pipelineElement.payload.DOM, this.rawPipelineModel);
+    //   }
+    // });
+    let pipelineElementType = PipelineElementTypeUtils.fromType(this.pipelineElement.payload);
+    this.pipelineElementCssType = PipelineElementTypeUtils.toCssShortHand(pipelineElementType);
+
+    if (this.pipelineElement.type === 'stream') {
+      this.initRecs(this.pipelineElement.payload.dom, this.rawPipelineModel);
+    }
+  }
+
+  removeElement(pipelineElement) {
+    this.delete.emit(pipelineElement);
+    //this.$rootScope.$broadcast("pipeline.validate");
+
+  }
+
+  openCustomizeDialog() {
+    let restrictedEditMode = ! (this.isRootElement());
+    // this.EditorDialogManager.showCustomizeDialog($("#" + this.pipelineElement.payload.dom), "", this.pipelineElement.payload, restrictedEditMode)
+    //     .then(() => {
+    //       this.JsplumbService.activateEndpoint(this.pipelineElement.payload.dom, !this.pipelineElement.payload.uncompleted);
+    //     }, () => {
+    //       this.JsplumbService.activateEndpoint(this.pipelineElement.payload.dom, !this.pipelineElement.payload.uncompleted);
+    //     });
+  }
+
+  openHelpDialog() {
+    //this.EditorDialogManager.openHelpDialog(this.pipelineElement.payload);
+  }
+
+  openCustomizeStreamDialog() {
+    //this.EditorDialogManager.showCustomizeStreamDialog(this.pipelineElement.payload);
+  }
+
+  initRecs(elementId, currentPipelineElements) {
+    // var currentPipeline = this.ObjectProvider.makePipeline(angular.copy(currentPipelineElements));
+    // this.PipelineElementRecommendationService.getRecommendations(this.allElements, currentPipeline).then((result) => {
+    //   if (result.success) {
+    //     this.possibleElements = result.possibleElements;
+    //     this.recommendedElements = result.recommendations;
+    //     this.recommendationsAvailable = true;
+    //     //this.InitTooltips.initTooltips();
+    //   }
+    // });
+  }
+
+  showRecommendations(e) {
+    this.recommendationsShown = !this.recommendationsShown;
+    e.stopPropagation();
+  }
+
+  isRootElement() {
+    return this.JsplumbBridge.getConnections({source: this.pipelineElement.payload.dom}).length === 0;
+  }
+
+  isConfigured() {
+    if (this.pipelineElement.type == 'stream') return true;
+    else {
+      return (this.pipelineElement.payload as InvocablePipelineElementUnion).configured;
+    }
+  }
+
+  isWildcardTopic() {
+    return (this.pipelineElement
+        .payload as SpDataStream)
+        .eventGrounding
+        .transportProtocols[0]
+        .topicDefinition instanceof WildcardTopicDefinition;
+  }
+}
\ No newline at end of file
diff --git a/ui/src/app/editor-v2/components/pipeline/pipeline.component.html b/ui/src/app/editor-v2/components/pipeline/pipeline.component.html
index 9cb66bb..634fd82 100644
--- a/ui/src/app/editor-v2/components/pipeline/pipeline.component.html
+++ b/ui/src/app/editor-v2/components/pipeline/pipeline.component.html
@@ -33,15 +33,15 @@
             </div>
             <pipeline-element [pipelineElement]="pipelineElement.payload" [preview]="preview"></pipeline-element>
         </span>
-<!--         <pipeline-element-options ng-if="!ctrl.preview" delete-function="ctrl.handleDeleteOption"-->
-<!--                                   current-mouse-over-element="ctrl.currentMouseOverElement"-->
-<!--                                   pipeline-valid="ctrl.pipelineValid"-->
-<!--                                   all-elements="ctrl.allElements"-->
-<!--                                   pipeline-element="pipelineElement"-->
-<!--                                   raw-pipeline-model="ctrl.rawPipelineModel"-->
-<!--                                   pipeline-element-id="pipelineElement.type == 'stream' ? pipelineElement.payload.elementId : pipelineElement.payload.belongsTo)"-->
-<!--                                   internal-id="" {{pipelineElement.payload.DOM}}">-->
-<!--        </pipeline-element-options>-->
+         <pipeline-element-options *ngIf="!preview" (delete)="handleDeleteOption($event)"
+                                   [currentMouseOverElement]="currentMouseOverElement"
+                                   [pipelineValid]="pipelineValid"
+                                   [allElements]="allElements"
+                                   [pipelineElement]="pipelineElement"
+                                   [rawPipelineModel]="rawPipelineModel"
+                                   [pipelineElementId]="pipelineElement.type == 'stream' ? pipelineElement.payload.elementId : pipelineElement.payload.belongsTo"
+                                   [internalId]="pipelineElement.payload.dom">
+        </pipeline-element-options>
     </span>
 </div>
 </div>
\ No newline at end of file
diff --git a/ui/src/app/editor-v2/components/pipeline/pipeline.component.ts b/ui/src/app/editor-v2/components/pipeline/pipeline.component.ts
index cb8e6f7..2b56f80 100644
--- a/ui/src/app/editor-v2/components/pipeline/pipeline.component.ts
+++ b/ui/src/app/editor-v2/components/pipeline/pipeline.component.ts
@@ -70,7 +70,7 @@ export class PipelineComponent implements OnInit {
   DialogBuilder: any;
   plumbReady: any;
   EditorDialogManager: any;
-  currentMouseOverElement: any;
+  currentMouseOverElement: string;
   currentPipelineModel: Pipeline;
   idCounter: any;
   currentZoomLevel: any;
@@ -226,10 +226,10 @@ export class PipelineComponent implements OnInit {
     }
   }
 
-  handleDeleteOption(pipelineElement) {
+  handleDeleteOption(pipelineElement: PipelineElementConfig) {
     this.JsplumbBridge.removeAllEndpoints(pipelineElement.payload.dom);
     angular.forEach(this.rawPipelineModel, pe => {
-      if (pe.payload.dom == pipelineElement.payload.DOM) {
+      if (pe.payload.dom == pipelineElement.payload.dom) {
         pe.settings.disabled = true;
       }
     });
diff --git a/ui/src/app/editor-v2/editor.module.ts b/ui/src/app/editor-v2/editor.module.ts
index 24499c0..25bfd62 100644
--- a/ui/src/app/editor-v2/editor.module.ts
+++ b/ui/src/app/editor-v2/editor.module.ts
@@ -41,6 +41,8 @@ import {PipelineEditorService} from "./services/pipeline-editor.service";
 import {PipelineValidationService} from "./services/pipeline-validation.service";
 import {PipelineComponent} from "./components/pipeline/pipeline.component";
 import {ObjectProvider} from "./services/object-provider.service";
+import {PipelineElementOptionsComponent} from "./components/pipeline-element-options/pipeline-element-options.component";
+import {PipelineElementRecommendationService} from "./services/pipeline-element-recommendation.service";
 
 @NgModule({
     imports: [
@@ -59,6 +61,7 @@ import {ObjectProvider} from "./services/object-provider.service";
         PipelineAssemblyComponent,
         PipelineElementIconStandComponent,
         PipelineElementComponent,
+        PipelineElementOptionsComponent,
         PipelineComponent
     ],
     providers: [
@@ -76,6 +79,7 @@ import {ObjectProvider} from "./services/object-provider.service";
         PipelineEditorService,
         PipelinePositioningService,
         PipelineValidationService,
+        PipelineElementRecommendationService,
         ElementIconText,
         ImageChecker,
         {
diff --git a/ui/src/app/editor-v2/services/jsplumb.service.ts b/ui/src/app/editor-v2/services/jsplumb.service.ts
index 79febe8..e91e841 100644
--- a/ui/src/app/editor-v2/services/jsplumb.service.ts
+++ b/ui/src/app/editor-v2/services/jsplumb.service.ts
@@ -21,6 +21,7 @@ import {JsplumbBridge} from "./jsplumb-bridge.service";
 import {Inject, Injectable} from "@angular/core";
 import {PipelineElementConfig, PipelineElementUnion} from "../model/editor.model";
 import {PipelineElementTypeUtils} from "../utils/editor.utils";
+import * as angular from "angular";
 
 @Injectable()
 export class JsplumbService {
@@ -146,7 +147,7 @@ export class JsplumbService {
         let pipelineElementConfig = {} as PipelineElementConfig;
         pipelineElementConfig.type = PipelineElementTypeUtils
             .toCssShortHand(PipelineElementTypeUtils.fromType(pipelineElement))
-        pipelineElementConfig.payload = pipelineElement;
+        pipelineElementConfig.payload = angular.copy(pipelineElement)
         pipelineElementConfig.settings = {connectable: connectable,
             openCustomize: !(pipelineElement as any).configured,
             preview: isPreview,
diff --git a/ui/src/app/editor/services/pipeline-element-recommendation.service.ts b/ui/src/app/editor-v2/services/pipeline-element-recommendation.service.ts
similarity index 93%
rename from ui/src/app/editor/services/pipeline-element-recommendation.service.ts
rename to ui/src/app/editor-v2/services/pipeline-element-recommendation.service.ts
index 07140fd..44eb9d9 100644
--- a/ui/src/app/editor/services/pipeline-element-recommendation.service.ts
+++ b/ui/src/app/editor-v2/services/pipeline-element-recommendation.service.ts
@@ -17,13 +17,13 @@
  */
 
 import * as angular from 'angular';
+import {Injectable} from "@angular/core";
+import {RestApi} from "../../services/rest-api.service";
 
+@Injectable()
 export class PipelineElementRecommendationService {
 
-    RestApi: any;
-
-    constructor(RestApi) {
-        this.RestApi = RestApi;
+    constructor(private RestApi: RestApi) {
     }
 
     getRecommendations(allElements, currentPipeline) {
diff --git a/ui/src/app/editor/editor.module.ts b/ui/src/app/editor/editor.module.ts
index 2a9b2c7..81df33b 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -53,7 +53,7 @@ import {PipelineComponent} from './components/pipeline/pipeline.component';
 import {EditorDialogManager} from '../editor-v2/services/editor-dialog-manager.service';
 import {PipelineElementComponent} from './components/pipeline-element/pipeline-element.component';
 import {PipelineElementRecommendationComponent} from "./components/pipeline-element-recommendation/pipeline-element-recommendation.component";
-import {PipelineElementRecommendationService} from "./services/pipeline-element-recommendation.service";
+import {PipelineElementRecommendationService} from "../editor-v2/services/pipeline-element-recommendation.service";
 import {PipelineAssemblyComponent} from "./components/pipeline-assembly/pipeline-assembly.component";
 import {PipelineElementIconStandComponent} from './components/pipeline-element-icon-stand/pipeline-element-icon-stand.component';
 import {PipelineValidationService} from "../editor-v2/services/pipeline-validation.service";