You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by wi...@apache.org on 2021/03/17 14:13:36 UTC
[incubator-streampipes] 01/02: Add retry option when pipeline
element container not ready
This is an automated email from the ASF dual-hosted git repository.
wiener pushed a commit to branch edge-extensions
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git
commit 21e2c945b0a0a448fcd67d0491e914873725d782
Merge: 45b1f3c 2f10a17
Author: Patrick Wiener <wi...@fzi.de>
AuthorDate: Wed Mar 17 13:08:09 2021 +0100
Add retry option when pipeline element container not ready
.../backend/StreamPipesBackendApplication.java | 30 ++-
.../api/AbstractPipelineElementResource.java | 14 ++
.../api/InvocablePipelineElementResource.java | 42 ++--
.../container/init/RunningInstances.java | 4 +
.../dataexplorer/DataLakeManagementV3.java | 2 +-
.../management/pe/InvocableElementManager.java | 58 ++++--
.../controller/container/storage/MapDBImpl.java | 6 +-
ui/angular.json | 2 -
ui/package.json | 5 +-
.../node-configuration.component.scss | 1 +
.../pipeline-assembly.component.html | 112 +++++++----
.../pipeline-assembly.component.scss | 121 +++++++++++-
.../pipeline-assembly.component.ts | 143 ++++++++++----
.../pipeline-element-options.component.ts | 8 +-
.../components/pipeline/pipeline.component.html | 10 +-
.../components/pipeline/pipeline.component.ts | 59 ++++--
ui/src/app/editor/editor.component.html | 37 ++--
ui/src/app/editor/editor.component.scss | 6 +-
ui/src/app/editor/editor.component.ts | 12 +-
ui/src/app/editor/editor.module.ts | 16 +-
.../enabled-pipeline-element.filter.ts} | 23 ++-
ui/src/app/editor/model/editor.model.ts | 7 +-
.../jsplumb.model.ts} | 19 +-
.../app/editor/services/jsplumb-bridge.service.ts | 79 +++++---
.../app/editor/services/jsplumb-config.service.ts | 48 ++---
.../editor/services/jsplumb-endpoint.service.ts | 63 ++++++
.../app/editor/services/jsplumb-factory.service.ts | 85 ++++++++
ui/src/app/editor/services/jsplumb.service.ts | 219 +++++++++------------
.../app/editor/services/object-provider.service.ts | 24 ++-
.../pipeline-canvas-scrolling.service.ts} | 24 ++-
.../app/editor/services/pipeline-editor.service.ts | 19 +-
.../pipeline-element-dragged.service.ts} | 20 +-
.../pipeline-element-recommendation.service.ts | 8 +-
.../services/pipeline-positioning.service.ts | 124 ++++++------
.../editor/services/pipeline-validation.service.ts | 42 ++--
.../preview/pipeline-preview.component.ts | 8 +-
ui/src/scss/_variables.scss | 3 +-
ui/src/scss/main.scss | 6 +-
.../editor.component.scss => scss/sp/jsplumb.scss} | 16 +-
ui/src/scss/sp/main.scss | 33 +---
40 files changed, 999 insertions(+), 559 deletions(-)
diff --cc streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
index a56a7d3,4530f8a..0b55885
--- a/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
+++ b/streampipes-container/src/main/java/org/apache/streampipes/container/api/InvocablePipelineElementResource.java
@@@ -70,15 -72,19 +72,19 @@@ public abstract class InvocablePipeline
RunningInstances.INSTANCE.add(runningInstanceId, graph, declarer.getClass().newInstance());
Response resp = RunningInstances.INSTANCE.getInvocation(runningInstanceId).invokeRuntime(graph);
return ok(resp);
+ } else {
+ LOG.info("Pipeline element {} with id {} seems to be already running, skipping invocation request.", graph.getName(), runningInstanceId);
- Response resp = new Response(graph.getElementId(), true);
++ Response resp = new Response(graph.getElementId(), true, "Pipeline element already running");
+ return ok(resp);
}
- } catch (InstantiationException | IllegalAccessException e) {
- e.printStackTrace();
- return ok(new Response(elementId, false, e.getMessage()));
- }
- return ok(new Response(elementId, false, "Could not find the element with id: " + elementId));
+ }
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ return ok(new Response(elementId, false, e.getMessage()));
}
- return ok(new Response(elementId, true, "Already running element with id: " + elementId));
+
+ return ok(new Response(elementId, false, "Could not find the element with id: " + elementId));
}
@POST
diff --cc streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/management/pe/InvocableElementManager.java
index 04cf003,0000000..848f243
mode 100644,000000..100644
--- a/streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/management/pe/InvocableElementManager.java
+++ b/streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/management/pe/InvocableElementManager.java
@@@ -1,301 -1,0 +1,333 @@@
+/*
+ * 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.node.controller.container.management.pe;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.gson.JsonSyntaxException;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.container.model.node.InvocableRegistration;
+import org.apache.streampipes.messaging.EventProducer;
+import org.apache.streampipes.messaging.jms.ActiveMQPublisher;
+import org.apache.streampipes.messaging.kafka.SpKafkaProducer;
+import org.apache.streampipes.messaging.mqtt.MqttPublisher;
+import org.apache.streampipes.model.Response;
+import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
+import org.apache.streampipes.model.grounding.*;
+import org.apache.streampipes.model.node.NodeInfoDescription;
+import org.apache.streampipes.model.pipeline.PipelineElementReconfigurationEntity;
+import org.apache.streampipes.model.staticproperty.FreeTextStaticProperty;
+import org.apache.streampipes.node.controller.container.config.NodeControllerConfig;
+import org.apache.streampipes.node.controller.container.management.node.NodeManager;
+import org.apache.streampipes.serializers.json.JacksonSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
++import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
++import java.net.Socket;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class InvocableElementManager implements PipelineElementLifeCycle {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(InvocableElementManager.class.getCanonicalName());
+
+ private static final String HTTP_PROTOCOL = "http://";
+ private static final String COLON = ":";
+ private static final String SLASH = "/";
+ private static final String DOT = ".";
+ private static final String SP_URL = "SP_URL";
+ private static final String CONSUL_LOCATION = "CONSUL_LOCATION";
+ private static final String CONSUL_REGISTRATION_ROUTE = "v1/agent/service/register";
+ private static final String RECONFIGURATION_TOPIC = "org.apache.streampipes.control.event.reconfigure";
+ private static final int CONSUL_DEFAULT_PORT = 8500;
- private static final Integer CONNECT_TIMEOUT = 10000;
++ private static final Integer CONNECT_TIMEOUT_MS = 10000;
++ private static final long RETRY_INTERVAL_MS = 5000;
+ private static InvocableElementManager instance = null;
+
+ private InvocableElementManager() {}
+
+ public static InvocableElementManager getInstance() {
+ if (instance == null) {
+ synchronized (InvocableElementManager.class) {
+ if (instance == null)
+ instance = new InvocableElementManager();
+ }
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(InvocableRegistration registration) {
+ registerAtConsul(registration);
+ updateAndSyncNodeInfoDescription(registration);
+ invokePipelineElementsOnSystemRebootOrRestart();
+ LOG.info("Successfully registered pipeline element container");
+ }
+
+ @Override
+ public Response invoke(InvocableStreamPipesEntity graph) {
++
++ Response response = new Response();
+ String endpoint = graph.getBelongsTo();
- LOG.info("Invoke pipeline element: {}", endpoint);
- try {
- org.apache.http.client.fluent.Response httpResp = Request
- .Post(endpoint)
- .bodyString(toJson(graph), ContentType.APPLICATION_JSON)
- .connectTimeout(CONNECT_TIMEOUT)
- .execute();
- return handleResponse(httpResp);
- } catch (Exception e) {
- LOG.error(e.getMessage());
++ String body = toJson(graph);
++ LOG.info("Trying to invoke pipeline element: {}", endpoint);
++
++ boolean connected = false;
++ while (!connected) {
++
++ connected = post(endpoint, body, response);
++
++ if (!connected) {
++ LOG.info("Retrying in {} seconds", (RETRY_INTERVAL_MS / 1000));
++ try {
++ Thread.sleep(RETRY_INTERVAL_MS);
++ } catch (InterruptedException e) {
++ throw new RuntimeException("Failed to invoke pipeline element: " + endpoint);
++ }
++ }
+ }
- throw new RuntimeException("Failed to invoke pipeline element: " + endpoint);
++ LOG.info("Successfully invoked pipeline element {}", endpoint);
++ return response;
+ }
+
+ @Override
+ public Response detach(String endpoint) {
+ LOG.info("Detach pipeline element: {}", endpoint);
+ try {
+ org.apache.http.client.fluent.Response httpResp = Request
+ .Delete(endpoint)
- .connectTimeout(CONNECT_TIMEOUT)
++ .connectTimeout(CONNECT_TIMEOUT_MS)
+ .execute();
+ return handleResponse(httpResp);
+ } catch (Exception e) {
+ LOG.error(e.getMessage());
+ }
+ throw new IllegalArgumentException("Failed to detach pipeline element: " + endpoint);
+ }
+
+ @Override
+ public void unregister(){
+ // TODO: unregister element from Consul and
+ setSupportedPipelineElements(Collections.emptyList());
+ try {
+ String url = generateBackendEndpoint();
+ String desc = toJson(getNodeInfoDescription());
+ Request.Put(url)
+ .bodyString(desc, ContentType.APPLICATION_JSON)
+ .execute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public Response adapt(InvocableStreamPipesEntity graph, PipelineElementReconfigurationEntity reconfigurationEntity) {
+
+ Response response = new Response();
+ response.setElementId(graph.getElementId());
+ response.setSuccess(false);
+
+ EventProducer pub = getReconfigurationEventProducerFromInvocable(graph);
+
+ byte [] reconfigurationEvent = reconfigurationToByteArray(reconfigurationEntity);
+ pub.publish(reconfigurationEvent);
+ pub.disconnect();
+
+ response.setSuccess(true);
+ return response;
+ }
+
++ private boolean post(String endpoint, String body, Response response) {
++ try {
++ org.apache.http.client.fluent.Response httpResp = Request
++ .Post(endpoint)
++ .bodyString(body, ContentType.APPLICATION_JSON)
++ .connectTimeout(CONNECT_TIMEOUT_MS)
++ .execute();
++ Response resp = handleResponse(httpResp);
++
++ response.setElementId(resp.getElementId());
++ response.setSuccess(resp.isSuccess());
++ response.setOptionalMessage(resp.getOptionalMessage());
++
++ return true;
++ } catch (IOException e) {
++ LOG.error("Could not invoke pipeline element - is the pipeline element container ready?");
++ }
++ return false;
++ }
++
+ public void invokePipelineElementsOnSystemRebootOrRestart() {
+ LOG.info("Checking for pipeline elements to be started ...");
+ getAllInvocables()
+ .forEach(graph -> {
+ Response status = InvocableElementManager.getInstance().invoke(graph);
+ if (status.isSuccess()) {
+ if (status.getOptionalMessage().isEmpty()) {
+ LOG.info("Pipeline element successfully restarted {}", status.getElementId());
+ } else {
+ LOG.info("Pipeline element already running {}", status.getElementId());
+ }
+ } else {
+ LOG.error("Pipeline element could not be restarted - are the pipeline element containers " +
+ "running? {}", status.getElementId());
+ }
+ });
+ }
+
+ private EventProducer getReconfigurationEventProducerFromInvocable(InvocableStreamPipesEntity graph) {
+ TransportProtocol tp = getReconfigurationTransportProtocol(graph);
+ EventProducer pub;
+ if(tp instanceof KafkaTransportProtocol){
+ pub = new SpKafkaProducer();
+ pub.connect(tp);
+ } else if (tp instanceof JmsTransportProtocol){
+ pub = new ActiveMQPublisher();
+ pub.connect(tp);
+ } else{
+ pub = new MqttPublisher();
+ pub.connect(tp);
+ }
+ return pub;
+ }
+
+ private byte[] reconfigurationToByteArray(PipelineElementReconfigurationEntity entity) {
+ Map<String, String> reconfigurationEventMap = new HashMap<>();
+ entity.getReconfiguredStaticProperties().forEach(staticProperty -> {
+ if (staticProperty instanceof FreeTextStaticProperty) {
+ reconfigurationEventMap.put(staticProperty.getInternalName(),
+ ((FreeTextStaticProperty) staticProperty).getValue());
+ }
+ });
+ return toJson(reconfigurationEventMap).getBytes(StandardCharsets.UTF_8);
+ }
+
+ private void updateAndSyncNodeInfoDescription(InvocableRegistration registration) {
+ setSupportedPipelineElements(registration.getSupportedPipelineElementAppIds());
+ try {
+ String url = generateBackendEndpoint();
+ String desc = toJson(getNodeInfoDescription());
+ Request.Put(url)
+ .bodyString(desc, ContentType.APPLICATION_JSON)
+ .execute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private List<InvocableStreamPipesEntity> getAllInvocables() {
+ return RunningInvocableInstances.INSTANCE.getAll();
+ }
+
+ private NodeInfoDescription getNodeInfoDescription() {
+ return NodeManager.getInstance().retrieveNodeInfoDescription();
+ }
+
+ private void setSupportedPipelineElements(List<String> supportedPipelineElements) {
+ NodeManager.getInstance()
+ .retrieveNodeInfoDescription()
+ .setSupportedElements(supportedPipelineElements);
+ }
+
+ private TransportProtocol getReconfigurationTransportProtocol(InvocableStreamPipesEntity graph) {
+ TransportProtocol tp = graph.getInputStreams().get(0).getEventGrounding().getTransportProtocol();
+ tp.setTopicDefinition(makeReconfigurationTopic(graph.getDeploymentRunningInstanceId()));
+ return tp;
+ }
+
+ private TopicDefinition makeReconfigurationTopic(String runningInstanceId) {
+ return new SimpleTopicDefinition( RECONFIGURATION_TOPIC + DOT + runningInstanceId);
+ }
+
+ private String generateBackendEndpoint() {
+ return HTTP_PROTOCOL
+ + NodeControllerConfig.INSTANCE.backendLocation()
+ + COLON
+ + NodeControllerConfig.INSTANCE.backendPort()
+ + SLASH
+ + "streampipes-backend/api/v2/users/admin@streampipes.org/nodes"
+ + SLASH
+ + NodeControllerConfig.INSTANCE.getNodeControllerId();
+ }
+
+ private void registerAtConsul(InvocableRegistration registration) {
+ try {
+ String endpoint = consulURL().toString() + SLASH + CONSUL_REGISTRATION_ROUTE;
+ Request.Put(endpoint)
+ .addHeader("accept", "application/json")
+ .body(new StringEntity(toJson(registration.getConsulServiceRegistrationBody())))
+ .execute();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Response handleResponse(org.apache.http.client.fluent.Response httpResp) throws JsonSyntaxException,
+ IOException {
+ String resp = httpResp.returnContent().asString();
+ return JacksonSerializer
+ .getObjectMapper()
+ .readValue(resp, Response.class);
+ }
+
+ private static URL consulURL() {
+ Map<String, String> env = System.getenv();
+ URL url = null;
+
+ if (env.containsKey(SP_URL)) {
+ try {
+ URL coreServerUrl = new URL(env.get(SP_URL));
+ url = new URL("http", coreServerUrl.getHost(), CONSUL_DEFAULT_PORT, "");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ } else if (env.containsKey(CONSUL_LOCATION)) {
+ try {
+ url = new URL("http", env.get(CONSUL_LOCATION), CONSUL_DEFAULT_PORT, "");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ } else {
+ try {
+ url = new URL("http", "localhost", CONSUL_DEFAULT_PORT, "");
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ return url;
+ }
+
+ private <T> String toJson(T element) {
+ try {
+ return JacksonSerializer.getObjectMapper().writeValueAsString(element);
+ } catch (JsonProcessingException e) {
+ throw new SpRuntimeException("Could not serialize object: " + element, e);
+ }
+ }
+
+}
diff --cc streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/storage/MapDBImpl.java
index f07302c,0000000..1916534
mode 100644,000000..100644
--- a/streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/storage/MapDBImpl.java
+++ b/streampipes-node-controller-container/src/main/java/org/apache/streampipes/node/controller/container/storage/MapDBImpl.java
@@@ -1,80 -1,0 +1,84 @@@
+/*
+ * 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.node.controller.container.storage;
+
+import org.mapdb.DB;
+import org.mapdb.DBMaker;
+import org.mapdb.Serializer;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+public class MapDBImpl implements CRUDStorage {
+
+ private static final String DB_STORAGE_PATH = "/var/lib/streampipes/";
+
+ private DB db;
+ private ConcurrentMap<String, Object> map;
+
+ public MapDBImpl(File dbFile) {
+ if("true".equals(System.getenv("SP_DEBUG"))) {
++// db = DBMaker
++// .memoryDB()
++// .closeOnJvmShutdown()
++// .make();
+ db = DBMaker
- .memoryDB()
++ .fileDB(dbFile)
+ .closeOnJvmShutdown()
+ .make();
+ } else {
+ db = DBMaker
+ .fileDB(DB_STORAGE_PATH + dbFile)
+ .closeOnJvmShutdown()
+ .make();
+ }
+ map = db.hashMap("nodectlcache", Serializer.STRING, Serializer.JAVA)
+ .createOrOpen();
+ }
+
+ @Override
+ public <T> void create(String id, T value) {
+ map.put(id, value);
+ }
+
+ @Override
+ public <T> T retrieve(String id) {
+ return (T) map.get(id);
+ }
+
+ @Override
+ public <T> List<T> retrieveAll() {
+ return map.values().stream().map(v -> (T) v).collect(Collectors.toList());
+ }
+
+ @Override
+ public <T> void update(String id, T value) {
+ map.put(id, value);
+ }
+
+ @Override
+ public void delete(String id) {
+ map.remove(id);
+ }
+
+ public void close() {
+ db.close();
+ }
+}
diff --cc ui/package.json
index 29ff920,efc485d..704e013
--- a/ui/package.json
+++ b/ui/package.json
@@@ -65,7 -65,7 +65,8 @@@
"konva": "3.2.4",
"leaflet": "1.6.0",
"lodash": "4.17.20",
+ "material-design-icons": "^3.0.1",
+ "material-icons": "^0.5.3",
"ng-file-upload": "9.0.13",
"ng-pick-datetime": "7.0.0",
"ng-prettyjson": "0.1.8",
diff --cc ui/src/app/configuration/node-configuration/node-configuration.component.scss
index 4e4fe65,0000000..ad31613
mode 100644,000000..100644
--- a/ui/src/app/configuration/node-configuration/node-configuration.component.scss
+++ b/ui/src/app/configuration/node-configuration/node-configuration.component.scss
@@@ -1,166 -1,0 +1,167 @@@
+/*
+ * 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 'src/scss/sp/colors';
+@import url("https://fonts.googleapis.com/icon?family=Material+Icons");
+
+.page-container-padding-inner {
+ padding: 10px;
+}
+
+.form-width {
+ width:80%;
+}
+
+.node-card {
+ max-width: 450px;
+}
+
+mat-card {
+ box-shadow: 0 2px 1px -1px rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .14), 0 1px 3px 0 rgba(0, 0, 0, .12);
+ margin: 10px;
+}
+
+.mat-card-header-text {
+ margin: 0 !important;
+}
+
+.node-mat-icon-button {
+ width: 25px !important;
+ height: 25px !important;
+ line-height: 25px !important;
+ padding: 2px;
+ min-width: 0;
+ flex-shrink: 0;
+ border-radius: 50%;
+ margin: 0 2px 0 2px;
+}
+
+li {
+ color: darkgrey;
+ font-size: 13px;
+}
+
+.div-node-tag{
+ display: inline-block;
+ margin-bottom: 10px;
+ }
+
+.span-node-tag{
+ font-size: 12px;
+ font-weight: bold;
+ color: whitesmoke;
+ background: dimgray;
+ padding: 5px;
+}
+
+.div-sa-tag{
+ display: inline-block;
+ margin-bottom: 10px;
+}
+
+.span-sa-tag{
+ font-size: 12px;
+ font-weight: bold;
+ color: whitesmoke;
+ background: lightslategray;
+ padding: 5px;
+}
+
+.node-header-avatar {
+ color: $sp-color-accent-light;
+ padding-top: 6px;
+}
+
+.div-icon-node-actions {
+ margin-left: auto;
+ margin-right: 0;
+}
+
+.node-state {
+ //float:right;
+ transform: scale(1.3);
+}
+
+.node-activated {
+ @extend .node-state;
+ color: $sp-color-primary;
+}
+
+.node-inactive {
+ @extend .node-state;
+ color: $sp-color-accent-light;
+}
+
+.node-deactivated {
+ @extend .node-state;
+ //color: $sp-color-accent;
+ color: #F44336;
+}
+
+.node-evict {
+ @extend .node-state;
+ color: $sp-color-accent;
+}
+
+/*.ft-foot-4 {*/
+/* column-count: 4;*/
+/* float: left;*/
+/* width: 100%;*/
+/* padding: 3px 0px;*/
+/* margin-bottom: 1px;*/
+/* text-align:center;*/
+/*}*/
+
+.ft-foot {
+ column-count: 3;
+ float: left;
+ width: 100%;
+ padding: 3px 0px;
+ margin-bottom: 1px;
+ text-align:center;
++ list-style: none;
+}
+.ft-foot .ft-foot-4 ul li {
+ display: inline-block;
+ margin:0 2px;
+}
+.inclusion {
+ color: darkgrey;
+ padding: 5px 0 5px 0;
+ /*display: block;*/
+ letter-spacing: 5px;
+ text-align: center;
+}
+.inclusion span {
+ display: block;
+ font-size: 10px;
+ text-transform: uppercase;
+}
+
+hr {
+ display: block;
+ height: 1px;
+ border: 0;
+ border-top: 1px solid #ccc;
+ margin: 0.6em 3em;
+ padding: 0;
+}
+
+.fa-custom {
+ font-size: 2em;
+}
diff --cc ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
index f8ac965,385d5c3..6cfd193
--- a/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
+++ b/ui/src/app/editor/components/pipeline-assembly/pipeline-assembly.component.ts
@@@ -31,7 -40,10 +40,11 @@@ import {ConfirmDialogComponent} from ".
import {MatDialog} from "@angular/material/dialog";
import {EditorService} from "../../services/editor.service";
import {PipelineService} from "../../../platform-services/apis/pipeline.service";
+import {pipe} from "rxjs";
+ import {PipelineCanvasScrollingService} from "../../services/pipeline-canvas-scrolling.service";
+ import {JsplumbFactoryService} from "../../services/jsplumb-factory.service";
+ import Panzoom, {PanzoomObject} from "@panzoom/panzoom";
+ import {PipelineElementDraggedService} from "../../services/pipeline-element-dragged.service";
@Component({
@@@ -60,6 -62,21 +63,22 @@@ export class PipelineAssemblyComponent
@Input()
allElements: PipelineElementUnion[];
+ @Output()
+ pipelineCanvasMaximizedEmitter: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+ JsplumbBridge: JsplumbBridge;
+
+ pipelineCanvasMaximized: boolean = false;
+
+ currentMouseOverElement: any;
+ currentZoomLevel: any;
+ preview: any;
+
+ selectMode: any;
+ currentPipelineName: any;
+ currentPipelineDescription: any;
++ currentEventRelayStrategy: any;
+
errorMessagesDisplayed: any = false;
pipelineValid: boolean = false;
diff --cc ui/src/app/editor/editor.module.ts
index f681b5b,b5c10e0..71e291d
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@@ -59,9 -59,12 +59,14 @@@ import {CustomOutputStrategyComponent}
import {PropertySelectionComponent} from "./components/output-strategy/property-selection/property-selection.component";
import {UserDefinedOutputStrategyComponent} from "./components/output-strategy/user-defined-output/user-defined-output.component";
import {ConnectModule} from "../connect/connect.module";
+import {MatSliderModule} from "@angular/material/slider";
+import { MigratePipelineProcessorsComponent } from './dialog/migrate-pipeline-processors/migrate-pipeline-processors.component';
import {PipelineElementTemplateConfigComponent} from "./components/pipeline-element-template-config/pipeline-element-template-config.component";
+ import {EnabledPipelineElementFilter} from "./filter/enabled-pipeline-element.filter";
+ import {PipelineElementDraggedService} from "./services/pipeline-element-dragged.service";
+ import {PipelineCanvasScrollingService} from "./services/pipeline-canvas-scrolling.service";
+ import {JsplumbEndpointService} from "./services/jsplumb-endpoint.service";
+ import {JsplumbFactoryService} from "./services/jsplumb-factory.service";
@NgModule({
imports: [
@@@ -79,7 -82,6 +84,8 @@@
MatProgressSpinnerModule,
ShowdownModule,
ReactiveFormsModule,
- MatSliderModule
++ MatSliderModule,
++ ReactiveFormsModule,
],
declarations: [
CompatibleElementsComponent,