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 2022/08/16 19:59:27 UTC

[incubator-streampipes] branch STREAMPIPES-577 updated: [STREAMPIPES-577] Add event preview to schema editor

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

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


The following commit(s) were added to refs/heads/STREAMPIPES-577 by this push:
     new c6be99b1e [STREAMPIPES-577] Add event preview to schema editor
c6be99b1e is described below

commit c6be99b1e6bfce65687cdb5c548a60c05fddf69a
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Tue Aug 16 21:58:21 2022 +0200

    [STREAMPIPES-577] Add event preview to schema editor
---
 .../master/management/GuessManagement.java         |   7 +
 .../streampipes/connect/adapter/Adapter.java       | 112 +--------------
 .../connect/adapter/AdapterPipelineGenerator.java  | 159 +++++++++++++++++++++
 .../pipeline/AdapterEventPreviewPipeline.java      |  81 +++++++++++
 .../model/connect/guess/AdapterEventPreview.java   |  40 ++++--
 .../rest/impl/connect/GuessResource.java           |  11 ++
 .../src/lib/model/gen/streampipes-model.ts         |  25 +++-
 .../event-property-row.component.html              | 117 ++++++++-------
 .../event-property-row.component.scss              |  61 ++++----
 .../event-property-row.component.ts                |  24 +++-
 .../event-schema-preview.component.html            |  24 +++-
 .../event-schema-preview.component.scss            |  11 ++
 .../event-schema-preview.component.ts              |  43 +++++-
 .../event-schema/event-schema.component.html       |  23 +--
 .../event-schema/event-schema.component.ts         |  36 ++++-
 .../schema-editor-header.component.ts              |   6 +-
 ui/src/app/connect/connect.module.ts               |   2 +
 .../json-pretty-print.pipe.ts}                     |  15 ++
 ui/src/app/connect/services/rest.service.ts        |  18 ++-
 19 files changed, 575 insertions(+), 240 deletions(-)

diff --git a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
index e54ade7a4..ce380864c 100644
--- a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
+++ b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/management/GuessManagement.java
@@ -26,17 +26,21 @@ import org.apache.http.client.fluent.Response;
 import org.apache.http.entity.ContentType;
 import org.apache.http.util.EntityUtils;
 import org.apache.streampipes.commons.exceptions.NoServiceEndpointsAvailableException;
+import org.apache.streampipes.connect.adapter.model.pipeline.AdapterEventPreviewPipeline;
 import org.apache.streampipes.connect.api.exception.ParseException;
 import org.apache.streampipes.connect.api.exception.WorkerAdapterException;
 import org.apache.streampipes.connect.container.master.util.WorkerPaths;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.connect.guess.AdapterEventPreview;
 import org.apache.streampipes.model.connect.guess.GuessSchema;
+import org.apache.streampipes.model.connect.guess.GuessTypeInfo;
 import org.apache.streampipes.model.message.ErrorMessage;
 import org.apache.streampipes.serializers.json.JacksonSerializer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.Map;
 
 public class GuessManagement {
 
@@ -74,4 +78,7 @@ public class GuessManagement {
             }
     }
 
+  public Map<String, GuessTypeInfo> performAdapterEventPreview(AdapterEventPreview previewRequest) {
+      return new AdapterEventPreviewPipeline(previewRequest).makePreview();
+  }
 }
diff --git a/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/Adapter.java b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/Adapter.java
index 52344b1bc..86fcf9bec 100644
--- a/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/Adapter.java
+++ b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/Adapter.java
@@ -18,32 +18,18 @@
 
 package org.apache.streampipes.connect.adapter;
 
-import org.apache.streampipes.config.backend.BackendConfig;
-import org.apache.streampipes.config.backend.SpProtocol;
 import org.apache.streampipes.connect.adapter.model.pipeline.AdapterPipeline;
-import org.apache.streampipes.connect.adapter.preprocessing.transform.stream.DuplicateFilterPipelineElement;
-import org.apache.streampipes.connect.api.IAdapterPipelineElement;
-import org.apache.streampipes.connect.adapter.preprocessing.elements.*;
+import org.apache.streampipes.connect.adapter.preprocessing.elements.SendToJmsAdapterSink;
+import org.apache.streampipes.connect.adapter.preprocessing.elements.SendToKafkaAdapterSink;
+import org.apache.streampipes.connect.adapter.preprocessing.elements.SendToMqttAdapterSink;
 import org.apache.streampipes.connect.api.IAdapter;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
-import org.apache.streampipes.model.connect.rules.TransformationRuleDescription;
-import org.apache.streampipes.model.connect.rules.stream.EventRateTransformationRuleDescription;
-import org.apache.streampipes.model.connect.rules.stream.RemoveDuplicatesTransformationRuleDescription;
-import org.apache.streampipes.model.connect.rules.value.AddTimestampRuleDescription;
-import org.apache.streampipes.model.connect.rules.value.AddValueTransformationRuleDescription;
-import org.apache.streampipes.model.connect.rules.value.CorrectionValueTransformationRuleDescription;
 import org.apache.streampipes.model.grounding.JmsTransportProtocol;
 import org.apache.streampipes.model.grounding.KafkaTransportProtocol;
 import org.apache.streampipes.model.grounding.MqttTransportProtocol;
 import org.apache.streampipes.model.grounding.TransportProtocol;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
 
 public abstract class Adapter<T extends AdapterDescription> implements IAdapter<T> {
-    Logger logger = LoggerFactory.getLogger(Adapter.class);
 
     private boolean debug;
 
@@ -99,97 +85,7 @@ public abstract class Adapter<T extends AdapterDescription> implements IAdapter<
     }
 
     private AdapterPipeline getAdapterPipeline(T adapterDescription) {
-
-        List<IAdapterPipelineElement> pipelineElements = new ArrayList<>();
-
-        // Must be before the schema transformations to ensure that user can move this event property
-        AddTimestampRuleDescription timestampTransformationRuleDescription = getTimestampRule(adapterDescription);
-        if (timestampTransformationRuleDescription != null) {
-            pipelineElements.add(new AddTimestampPipelineElement(
-                    timestampTransformationRuleDescription.getRuntimeKey()));
-        }
-
-        AddValueTransformationRuleDescription valueTransformationRuleDescription = getAddValueRule(adapterDescription);
-        if (valueTransformationRuleDescription != null) {
-            pipelineElements.add(new AddValuePipelineElement(
-                    valueTransformationRuleDescription.getRuntimeKey(),
-                    valueTransformationRuleDescription.getStaticValue()));
-        }
-
-
-        // first transform schema before transforming vales
-        // value rules should use unique keys for of new schema
-        pipelineElements.add(new TransformSchemaAdapterPipelineElement(adapterDescription.getSchemaRules()));
-        pipelineElements.add(new TransformValueAdapterPipelineElement(adapterDescription.getValueRules()));
-
-
-        RemoveDuplicatesTransformationRuleDescription duplicatesTransformationRuleDescription = getRemoveDuplicateRule(adapterDescription);
-        if (duplicatesTransformationRuleDescription != null) {
-            pipelineElements.add(new DuplicateFilterPipelineElement(duplicatesTransformationRuleDescription.getFilterTimeWindow()));
-        }
-
-        TransformStreamAdapterElement transformStreamAdapterElement = new TransformStreamAdapterElement();
-        EventRateTransformationRuleDescription eventRateTransformationRuleDescription = getEventRateTransformationRule(adapterDescription);
-        if (eventRateTransformationRuleDescription != null) {
-            transformStreamAdapterElement.addStreamTransformationRuleDescription(eventRateTransformationRuleDescription);
-        }
-        pipelineElements.add(transformStreamAdapterElement);
-
-        // Needed when adapter is (
-        if (adapterDescription.getEventGrounding() != null && adapterDescription.getEventGrounding().getTransportProtocol() != null
-                && adapterDescription.getEventGrounding().getTransportProtocol().getBrokerHostname() != null) {
-            return new AdapterPipeline(pipelineElements, getAdapterSink(adapterDescription));
-        }
-
-        return new AdapterPipeline(pipelineElements);
-    }
-
-    private SendToBrokerAdapterSink<?> getAdapterSink(AdapterDescription adapterDescription) {
-        SpProtocol prioritizedProtocol =
-                BackendConfig.INSTANCE.getMessagingSettings().getPrioritizedProtocols().get(0);
-
-        if (GroundingService.isPrioritized(prioritizedProtocol, JmsTransportProtocol.class)) {
-            return new SendToJmsAdapterSink(adapterDescription);
-        }
-        else if (GroundingService.isPrioritized(prioritizedProtocol, KafkaTransportProtocol.class)) {
-            return new SendToKafkaAdapterSink(adapterDescription);
-        }
-        else {
-            return new SendToMqttAdapterSink(adapterDescription);
-        }
-    }
-
-    private RemoveDuplicatesTransformationRuleDescription getRemoveDuplicateRule(T adapterDescription) {
-        return getRule(adapterDescription, RemoveDuplicatesTransformationRuleDescription.class);
-    }
-
-    private EventRateTransformationRuleDescription getEventRateTransformationRule(T adapterDescription) {
-        return getRule(adapterDescription, EventRateTransformationRuleDescription.class);
-    }
-
-    private AddTimestampRuleDescription getTimestampRule(T adapterDescription) {
-        return getRule(adapterDescription, AddTimestampRuleDescription.class);
-    }
-
-    private AddValueTransformationRuleDescription getAddValueRule(T adapterDescription) {
-        return getRule(adapterDescription, AddValueTransformationRuleDescription.class);
-    }
-
-    private CorrectionValueTransformationRuleDescription getCorrectionValueRule(T adapterDescription) {
-        return getRule(adapterDescription, CorrectionValueTransformationRuleDescription.class);
-    }
-
-    private <G extends TransformationRuleDescription> G getRule(T adapterDescription, Class<G> type) {
-
-        if (adapterDescription != null) {
-            for (TransformationRuleDescription tr : adapterDescription.getRules()) {
-                if (type.isInstance(tr)) {
-                    return type.cast(tr);
-                }
-            }
-        }
-
-        return null;
+        return new AdapterPipelineGenerator().generatePipeline(adapterDescription);
     }
 
     @Override
diff --git a/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/AdapterPipelineGenerator.java b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/AdapterPipelineGenerator.java
new file mode 100644
index 000000000..31ce0e9a1
--- /dev/null
+++ b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/AdapterPipelineGenerator.java
@@ -0,0 +1,159 @@
+/*
+ * 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.connect.adapter;
+
+import org.apache.streampipes.config.backend.BackendConfig;
+import org.apache.streampipes.config.backend.SpProtocol;
+import org.apache.streampipes.connect.adapter.model.pipeline.AdapterPipeline;
+import org.apache.streampipes.connect.adapter.preprocessing.elements.*;
+import org.apache.streampipes.connect.adapter.preprocessing.transform.stream.DuplicateFilterPipelineElement;
+import org.apache.streampipes.connect.api.IAdapterPipelineElement;
+import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.connect.rules.TransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.schema.SchemaTransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.stream.EventRateTransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.stream.RemoveDuplicatesTransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.value.AddTimestampRuleDescription;
+import org.apache.streampipes.model.connect.rules.value.AddValueTransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.value.CorrectionValueTransformationRuleDescription;
+import org.apache.streampipes.model.connect.rules.value.ValueTransformationRuleDescription;
+import org.apache.streampipes.model.grounding.JmsTransportProtocol;
+import org.apache.streampipes.model.grounding.KafkaTransportProtocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class AdapterPipelineGenerator {
+
+  // TODO improve this code
+  public AdapterPipeline generatePipeline(AdapterDescription adapterDescription) {
+
+    var pipelineElements = makeAdapterPipelineElements(adapterDescription.getRules());
+
+    RemoveDuplicatesTransformationRuleDescription duplicatesTransformationRuleDescription = getRemoveDuplicateRule(adapterDescription.getRules());
+    if (duplicatesTransformationRuleDescription != null) {
+      pipelineElements.add(new DuplicateFilterPipelineElement(duplicatesTransformationRuleDescription.getFilterTimeWindow()));
+    }
+
+    TransformStreamAdapterElement transformStreamAdapterElement = new TransformStreamAdapterElement();
+    EventRateTransformationRuleDescription eventRateTransformationRuleDescription = getEventRateTransformationRule(adapterDescription.getRules());
+    if (eventRateTransformationRuleDescription != null) {
+      transformStreamAdapterElement.addStreamTransformationRuleDescription(eventRateTransformationRuleDescription);
+    }
+    pipelineElements.add(transformStreamAdapterElement);
+
+    // TODO decide what was meant with this comment
+    // Needed when adapter is (
+    if (adapterDescription.getEventGrounding() != null && adapterDescription.getEventGrounding().getTransportProtocol() != null
+      && adapterDescription.getEventGrounding().getTransportProtocol().getBrokerHostname() != null) {
+      return new AdapterPipeline(pipelineElements, getAdapterSink(adapterDescription));
+    }
+
+    return new AdapterPipeline(pipelineElements);
+  }
+
+  public List<IAdapterPipelineElement> makeAdapterPipelineElements(List<TransformationRuleDescription> rules) {
+    List<IAdapterPipelineElement> pipelineElements = new ArrayList<>();
+
+    // Must be before the schema transformations to ensure that user can move this event property
+    AddTimestampRuleDescription timestampTransformationRuleDescription = getTimestampRule(rules);
+    if (timestampTransformationRuleDescription != null) {
+      pipelineElements.add(new AddTimestampPipelineElement(
+        timestampTransformationRuleDescription.getRuntimeKey()));
+    }
+
+    AddValueTransformationRuleDescription valueTransformationRuleDescription = getAddValueRule(rules);
+    if (valueTransformationRuleDescription != null) {
+      pipelineElements.add(new AddValuePipelineElement(
+        valueTransformationRuleDescription.getRuntimeKey(),
+        valueTransformationRuleDescription.getStaticValue()));
+    }
+
+    // first transform schema before transforming vales
+    // value rules should use unique keys for of new schema
+    pipelineElements.add(new TransformSchemaAdapterPipelineElement(getSchemaRules(rules)));
+    pipelineElements.add(new TransformValueAdapterPipelineElement(getValueRules(rules)));
+
+    return pipelineElements;
+  }
+
+  private SendToBrokerAdapterSink<?> getAdapterSink(AdapterDescription adapterDescription) {
+    SpProtocol prioritizedProtocol =
+      BackendConfig.INSTANCE.getMessagingSettings().getPrioritizedProtocols().get(0);
+
+    if (GroundingService.isPrioritized(prioritizedProtocol, JmsTransportProtocol.class)) {
+      return new SendToJmsAdapterSink(adapterDescription);
+    }
+    else if (GroundingService.isPrioritized(prioritizedProtocol, KafkaTransportProtocol.class)) {
+      return new SendToKafkaAdapterSink(adapterDescription);
+    }
+    else {
+      return new SendToMqttAdapterSink(adapterDescription);
+    }
+  }
+
+  private RemoveDuplicatesTransformationRuleDescription getRemoveDuplicateRule(List<TransformationRuleDescription> rules) {
+    return getRule(rules, RemoveDuplicatesTransformationRuleDescription.class);
+  }
+
+  private EventRateTransformationRuleDescription getEventRateTransformationRule(List<TransformationRuleDescription> rules) {
+    return getRule(rules, EventRateTransformationRuleDescription.class);
+  }
+
+  private AddTimestampRuleDescription getTimestampRule(List<TransformationRuleDescription> rules) {
+    return getRule(rules, AddTimestampRuleDescription.class);
+  }
+
+  private AddValueTransformationRuleDescription getAddValueRule(List<TransformationRuleDescription> rules) {
+    return getRule(rules, AddValueTransformationRuleDescription.class);
+  }
+
+  private CorrectionValueTransformationRuleDescription getCorrectionValueRule(List<TransformationRuleDescription> rules) {
+    return getRule(rules, CorrectionValueTransformationRuleDescription.class);
+  }
+
+  private <G extends TransformationRuleDescription> G getRule(List<TransformationRuleDescription> rules,
+                                                              Class<G> type) {
+
+    if (rules != null) {
+      for (TransformationRuleDescription tr : rules) {
+        if (type.isInstance(tr)) {
+          return type.cast(tr);
+        }
+      }
+    }
+
+    return null;
+  }
+
+  private List<TransformationRuleDescription> getValueRules(List<TransformationRuleDescription> rules) {
+    return rules
+      .stream()
+      .filter(r -> r instanceof ValueTransformationRuleDescription && !(r instanceof AddTimestampRuleDescription))
+      .collect(Collectors.toList());
+  }
+
+  private List<TransformationRuleDescription> getSchemaRules(List<TransformationRuleDescription> rules) {
+    return rules
+      .stream()
+      .filter(r -> r instanceof SchemaTransformationRuleDescription)
+      .collect(Collectors.toList());
+  }
+}
diff --git a/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/model/pipeline/AdapterEventPreviewPipeline.java b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/model/pipeline/AdapterEventPreviewPipeline.java
new file mode 100644
index 000000000..3e36ad5b4
--- /dev/null
+++ b/streampipes-connect/src/main/java/org/apache/streampipes/connect/adapter/model/pipeline/AdapterEventPreviewPipeline.java
@@ -0,0 +1,81 @@
+/*
+ * 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.connect.adapter.model.pipeline;
+
+import org.apache.streampipes.connect.adapter.AdapterPipelineGenerator;
+import org.apache.streampipes.connect.api.IAdapterPipeline;
+import org.apache.streampipes.connect.api.IAdapterPipelineElement;
+import org.apache.streampipes.model.connect.guess.AdapterEventPreview;
+import org.apache.streampipes.model.connect.guess.GuessTypeInfo;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class AdapterEventPreviewPipeline implements IAdapterPipeline {
+
+  private List<IAdapterPipelineElement> pipelineElements;
+  private Map<String, GuessTypeInfo> event;
+
+  public AdapterEventPreviewPipeline(AdapterEventPreview previewRequest) {
+    this.pipelineElements = new AdapterPipelineGenerator().makeAdapterPipelineElements(previewRequest.getRules());
+    this.event = previewRequest.getInputData();
+  }
+
+  @Override
+  public void process(Map<String, Object> event) {
+    for (IAdapterPipelineElement pe : this.pipelineElements) {
+      event = pe.process(event);
+    }
+  }
+
+  @Override
+  public List<IAdapterPipelineElement> getPipelineElements() {
+    return null;
+  }
+
+  @Override
+  public void setPipelineElements(List<IAdapterPipelineElement> pipelineElements) {
+
+  }
+
+  @Override
+  public void changePipelineSink(IAdapterPipelineElement pipelineSink) {
+
+  }
+
+  @Override
+  public IAdapterPipelineElement getPipelineSink() {
+    return null;
+  }
+
+  public Map<String, GuessTypeInfo> makePreview() {
+    Map<String, Object> ev = this.event
+      .entrySet()
+      .stream()
+      .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getValue()));
+    this.process(ev);
+
+    return ev
+      .entrySet()
+      .stream()
+      .collect(Collectors.toMap(Map.Entry::getKey, e-> new GuessTypeInfo(e.getValue().getClass().getCanonicalName(), e.getValue())));
+  }
+}
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/guess/AdapterEventPreview.java
similarity index 51%
copy from ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts
copy to streampipes-model/src/main/java/org/apache/streampipes/model/connect/guess/AdapterEventPreview.java
index f80de67d9..d70af4f3f 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/guess/AdapterEventPreview.java
@@ -16,14 +16,34 @@
  *
  */
 
-import { Component, Input } from '@angular/core';
-import { EventSchema } from '@streampipes/platform-services';
-
-@Component({
-    selector: 'sp-event-schema-preview',
-    templateUrl: './event-schema-preview.component.html',
-    styleUrls: ['./event-schema-preview.component.scss']
-})
-export class EventSchemaPreviewComponent {
-    @Input() eventSchema: EventSchema;
+package org.apache.streampipes.model.connect.guess;
+
+import org.apache.streampipes.model.connect.rules.TransformationRuleDescription;
+import org.apache.streampipes.model.shared.annotation.TsModel;
+
+import java.util.List;
+import java.util.Map;
+
+@TsModel
+public class AdapterEventPreview {
+
+  private List<TransformationRuleDescription> rules;
+
+  private Map<String, GuessTypeInfo> inputData;
+
+  public List<TransformationRuleDescription> getRules() {
+    return rules;
+  }
+
+  public void setRules(List<TransformationRuleDescription> rules) {
+    this.rules = rules;
+  }
+
+  public Map<String, GuessTypeInfo> getInputData() {
+    return inputData;
+  }
+
+  public void setInputData(Map<String, GuessTypeInfo> inputData) {
+    this.inputData = inputData;
+  }
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
index 7cdd81281..b678b9eab 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/GuessResource.java
@@ -24,11 +24,13 @@ import org.apache.streampipes.connect.api.exception.WorkerAdapterException;
 import org.apache.streampipes.connect.container.master.management.GuessManagement;
 import org.apache.streampipes.model.StreamPipesErrorMessage;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.connect.guess.AdapterEventPreview;
 import org.apache.streampipes.model.connect.guess.GuessSchema;
 import org.apache.streampipes.rest.shared.annotation.JacksonSerialized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -66,5 +68,14 @@ public class GuessResource extends AbstractAdapterResource<GuessManagement> {
         return serverError(StreamPipesErrorMessage.from(e));
       }
   }
+
+  @POST
+  @JacksonSerialized
+  @Path("/schema/preview")
+  @Produces(MediaType.APPLICATION_JSON)
+  @Consumes(MediaType.APPLICATION_JSON)
+  public Response getAdapterEventPreview(AdapterEventPreview previewRequest) {
+    return ok(managementService.performAdapterEventPreview(previewRequest));
+  }
 }
 
diff --git a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index 7d282d6de..d2712a990 100644
--- a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -18,7 +18,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2022-08-16 11:39:37.
+// Generated using typescript-generator version 2.27.744 on 2022-08-16 16:51:03.
 
 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 [...]
@@ -192,9 +192,9 @@ export class AdapterDescription extends NamedStreamPipesEntity {
         instance.selectedEndpointUrl = data.selectedEndpointUrl;
         instance.correspondingServiceGroup = data.correspondingServiceGroup;
         instance.correspondingDataStreamElementId = data.correspondingDataStreamElementId;
-        instance.valueRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.valueRules);
-        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
         instance.schemaRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.schemaRules);
+        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
+        instance.valueRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.valueRules);
         return instance;
     }
 
@@ -215,6 +215,21 @@ export class AdapterDescription extends NamedStreamPipesEntity {
     }
 }
 
+export class AdapterEventPreview {
+    inputData: { [index: string]: GuessTypeInfo };
+    rules: TransformationRuleDescriptionUnion[];
+
+    static fromData(data: AdapterEventPreview, target?: AdapterEventPreview): AdapterEventPreview {
+        if (!data) {
+            return data;
+        }
+        const instance = target || new AdapterEventPreview();
+        instance.rules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.rules);
+        instance.inputData = __getCopyObjectFn(GuessTypeInfo.fromData)(data.inputData);
+        return instance;
+    }
+}
+
 export class AdapterSetDescription extends AdapterDescription {
     "@class": "org.apache.streampipes.model.connect.adapter.AdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.GenericAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.SpecificAdapterSetDescription";
     dataSet: SpDataSet;
@@ -1753,8 +1768,8 @@ export class GenericAdapterSetDescription extends AdapterSetDescription implemen
         const instance = target || new GenericAdapterSetDescription();
         super.fromData(data, instance);
         instance.eventSchema = EventSchema.fromData(data.eventSchema);
-        instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
         instance.formatDescription = FormatDescription.fromData(data.formatDescription);
+        instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
         return instance;
     }
 }
@@ -1772,8 +1787,8 @@ export class GenericAdapterStreamDescription extends AdapterStreamDescription im
         const instance = target || new GenericAdapterStreamDescription();
         super.fromData(data, instance);
         instance.eventSchema = EventSchema.fromData(data.eventSchema);
-        instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
         instance.formatDescription = FormatDescription.fromData(data.formatDescription);
+        instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
         return instance;
     }
 }
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.html b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.html
index be87eb7e2..88a49c3e4 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.html
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.html
@@ -17,67 +17,78 @@
   -->
 
 <div fxLayout="row" fxFlex="100" fxLayoutGap="10px">
-    <div fxFlex fxLayout="row" fxLayoutAlign="start center">
-        <b>
+    <div fxLayout="row" fxLayoutAlign="start center">
+        <div>
+        <b style="white-space: nowrap">
             {{ label }}
         </b>
-        <div class="timestamp-property" *ngIf="timestampProperty" fxLayoutAlign="start center" fxLayout="row">
+        </div>
+        <div fxLayoutAlign="start center" *ngIf="originalRuntimeName" class="ml-5">
+            <span class="runtime-info">{{originalRuntimeName}}</span>
+            <span fxLayoutAlign="center center"
+                  *ngIf="originalRuntimeName !== node.data.runtimeName">
+                <i class="material-icons">arrow_right</i>
+            </span>
+            <span class="runtime-info"
+                  *ngIf="originalRuntimeName !== node.data.runtimeName">{{node.data.runtimeName}}
+            </span>
+        </div>
+        <p style="margin: 0px 10px 10px;" *ngIf="isList">[List]</p>
+    </div>
+
+    <div fxLayout="row" fxLayoutAlign="end center" fxFlex="100" fxLayoutGap="20px">
+        <div class="timestamp-property" *ngIf="timestampProperty" fxLayoutAlign="end center" fxLayout="row">
             <mat-icon *ngIf="timestampProperty" class="timestamp-icon" fxLayout="row" fxLayoutAlign="start center">
                 access_time
             </mat-icon>
             <span style="margin-left: -5px;">marked as timestamp</span>
         </div>
-        <p style="margin: 0px 10px 10px;" *ngIf="isList">[List]</p>
-    </div>
-    <div fxFlex="15" fxLayoutAlign="start center" *ngIf="runtimeType">
-        <span class="runtime-type-info">{{runtimeType}}</span>
-    </div>
-    <div fxFlex="15" fxLayoutAlign="start center">
-        <span *ngIf="showEventPreview">{{eventPreview[0][node.data.runtimeName].value || 'n/a'}}</span>
-    </div>
-    <div fxFlex="15" fxLayoutAlign="start center">
+        <div fxLayoutAlign="end center" *ngIf="runtimeType" class="runtime-type-info-outer">
+            <span class="runtime-info runtime-type-info">{{originalRuntimeType}}</span>
+            <span fxLayoutAlign="center center"><i class="material-icons">arrow_right</i></span>
+            <span class="runtime-info runtime-type-info">{{runtimeType}}</span>
+        </div>
+        <div fxLayoutAlign="end center">
         <span *ngIf="showFieldStatus"
-              [ngClass]="'status status-' +fieldStatusInfo[node.data.runtimeName].fieldStatus.toLowerCase()"
-        [matTooltip]="fieldStatusInfo[node.data.runtimeName].additionalInfo">{{fieldStatusInfo[node.data.runtimeName].fieldStatus}}</span>
-    </div>
-
-    <div fxLayout="row" fxLayoutAlign="end center">
-    <div fxFlex="15" *ngIf="isPrimitive" fxLayoutAlign="end center">
-        <mat-form-field class="small-select" color="accent">
-        <mat-select [(ngModel)]="node.data.propertyScope" panelClass="small-select"
-                    [attr.data-cy]="'property-scope-' + label">
-            <mat-option value="MEASUREMENT_PROPERTY">Measurement</mat-option>
-            <mat-option value="DIMENSION_PROPERTY">Dimension</mat-option>
-            <mat-option value="HEADER_PROPERTY">Header</mat-option>
-        </mat-select>
-        </mat-form-field>
-    </div>
+              [ngClass]="'status status-' +fieldStatusInfo[originalRuntimeName].fieldStatus.toLowerCase()"
+              [matTooltip]="fieldStatusInfo[originalRuntimeName].additionalInfo">{{fieldStatusInfo[originalRuntimeName].fieldStatus}}</span>
+        </div>
+        <div *ngIf="isPrimitive" fxLayoutAlign="end center">
+            <mat-form-field class="small-select" color="accent">
+                <mat-select [(ngModel)]="node.data.propertyScope" panelClass="small-select"
+                            [attr.data-cy]="'property-scope-' + label">
+                    <mat-option value="MEASUREMENT_PROPERTY">Measurement</mat-option>
+                    <mat-option value="DIMENSION_PROPERTY">Dimension</mat-option>
+                    <mat-option value="HEADER_PROPERTY">Header</mat-option>
+                </mat-select>
+            </mat-form-field>
+        </div>
 
-    <div fxLayoutAlign="end center"
-         *ngIf="isNested">
-        <button [disabled]="!isEditable" color="accent" mat-button (click)=addNestedProperty(node.data)>
-            <mat-icon matTooltip="Add a Nested Property">queue</mat-icon>
-        </button>
-    </div>
-    <div
-         fxLayoutAlign="end center"
-         class="ml-5 mr-5"
-         *ngIf="isNested || isPrimitive || isList">
-        <button [disabled]="!isEditable" color="accent" mat-button
-                (click)="openEditDialog(node.data)"
-                [attr.data-cy]="'edit-' + label.toLowerCase()">
-            <mat-icon>edit</mat-icon>&nbsp;Edit field
-        </button>
-    </div>
-    <div fxLayoutAlign="end center">
-        <mat-checkbox
-                *ngIf="isNested || isPrimitive || isList"
-                (click)="selectProperty(node.data.id, undefined)"
-                [disabled]="!isEditable"
-                [class.checkbox-selected]="node.data.selected"
-                [checked]="node.data.selected"
-                [attr.data-cy]="'delete-property-' + label.toLowerCase()">
-        </mat-checkbox>
-    </div>
+        <div fxLayoutAlign="end center"
+             *ngIf="isNested">
+            <button [disabled]="!isEditable" color="accent" mat-button (click)=addNestedProperty(node.data)>
+                <mat-icon matTooltip="Add a Nested Property">queue</mat-icon>
+            </button>
+        </div>
+        <div
+                fxLayoutAlign="end center"
+                class="ml-5 mr-5"
+                *ngIf="isNested || isPrimitive || isList">
+            <button [disabled]="!isEditable" color="accent" mat-button
+                    (click)="openEditDialog(node.data)"
+                    [attr.data-cy]="'edit-' + label.toLowerCase()">
+                <mat-icon>edit</mat-icon>&nbsp;Edit field
+            </button>
+        </div>
+        <div fxLayoutAlign="end center">
+            <mat-checkbox
+                    *ngIf="isNested || isPrimitive || isList"
+                    (click)="selectProperty(node.data.id, undefined)"
+                    [disabled]="!isEditable"
+                    [class.checkbox-selected]="node.data.selected"
+                    [checked]="node.data.selected"
+                    [attr.data-cy]="'delete-property-' + label.toLowerCase()">
+            </mat-checkbox>
+        </div>
     </div>
 </div>
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.scss b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.scss
index c20307e9b..9e4191930 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.scss
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.scss
@@ -18,54 +18,67 @@
 
 
 ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background, .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background {
-    background-color: var(--color-accent) !important;
+  background-color: var(--color-accent) !important;
 }
 
 ::ng-deep .mat-checkbox:not(.mat-checkbox-disabled).mat-accent .mat-checkbox-ripple .mat-ripple-element {
-    background-color: var(--color-accent) !important;
+  background-color: var(--color-accent) !important;
 }
 
 .checkbox-selected {
-    opacity: 1 !important;
+  opacity: 1 !important;
 }
 
 .timestamp-property {
-    margin-left: 15px;
-    border-radius: 10px;
-    background: var(--color-processor);
-    padding: 0px 10px;
-    font-size: 12px;
-    color: #FFFFFF;
+  margin-left: 15px;
+  border-radius: 10px;
+  background: var(--color-processor);
+  padding: 0px 10px;
+  font-size: 12px;
+  color: #FFFFFF;
 }
 
 .timestamp-icon {
-    font-size: 12px;
+  font-size: 12px;
 }
 
 .status-good {
-    background: #75c575;
-    color: white;
+  background: #75c575;
+  color: white;
 }
 
 .status-bad {
-    background: #d2a169;
-    color: white;
+  background: #d2a169;
+  color: white;
 }
 
 .status {
-    width: 60px;
-    text-align: center;
-    padding: 5px;
-    border-radius: 3px;
+  width: 70px;
+  min-width: 70px;
+  text-align: center;
+  padding: 5px;
+  border-radius: 5px;
+}
+
+.runtime-info {
+  border-radius: 5px;
+  color: var(--color-default-text);
+  font-size: 10pt;
+  text-align: center;
 }
 
 .runtime-type-info {
-    padding: 5px;
-    border-radius: 3px;
-    background: var(--color-bg-3);
-    color: var(--color-default-text);
-    width: 70px;
-    text-align: center;
+  border-radius: 5px;
+  color: var(--color-default-text);
+  width: 60px;
+  font-size: 10pt;
+  text-align: center;
+}
+
+.runtime-type-info-outer {
+  padding: 3px;
+  border-radius: 5px;
+  background: var(--color-bg-3);
 }
 
 
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.ts
index f2be21368..393066c93 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-property-row/event-property-row.component.ts
@@ -28,28 +28,30 @@ import {
   EventPropertyUnion,
   EventSchema,
   FieldStatusInfo,
+  GuessTypeInfo
 
 } from '@streampipes/platform-services';
 import { EditEventPropertyComponent } from '../../../../dialog/edit-event-property/edit-event-property.component';
 import { DialogService, PanelType } from '@streampipes/shared-ui';
-import { GuessTypeInfo } from '../../../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Component({
   selector: 'event-property-row',
   templateUrl: './event-property-row.component.html',
   styleUrls: ['./event-property-row.component.scss']
 })
-export class EventPropertyRowComponent implements OnInit, OnChanges {
+export class EventPropertyRowComponent implements OnInit {
 
   @Input() node: TreeNode;
   @Input() isEditable = true;
   @Input() eventSchema: EventSchema = new EventSchema();
+  @Input() originalEventSchema: EventSchema;
   @Input() countSelected: number;
   @Input() eventPreview: Record<string, GuessTypeInfo>[];
   @Input() fieldStatusInfo: Record<string, FieldStatusInfo>;
 
   @Output() isEditableChange = new EventEmitter<boolean>();
   @Output() eventSchemaChange = new EventEmitter<EventSchema>();
+  @Output() originalEventSchemaChange = new EventEmitter<EventSchema>();
   @Output() refreshTreeEmitter = new EventEmitter<void>();
   @Output() countSelectedChange = new EventEmitter<number>();
 
@@ -62,6 +64,8 @@ export class EventPropertyRowComponent implements OnInit, OnChanges {
   showFieldStatus = false;
 
   runtimeType: string;
+  originalRuntimeType: string;
+  originalRuntimeName: string;
 
   constructor(private dialog: MatDialog,
               private dialogService: DialogService) {
@@ -69,16 +73,20 @@ export class EventPropertyRowComponent implements OnInit, OnChanges {
   }
 
   ngOnInit() {
-    this.showFieldStatus = this.fieldStatusInfo && this.fieldStatusInfo[this.node.data.runtimeName] !== undefined;
-    this.showEventPreview = this.eventPreview && this.eventPreview.length > 0 && this.eventPreview[0][this.node.data.runtimeName] !== undefined;
     this.label = this.getLabel(this.node.data);
     this.isPrimitive = this.isEventPropertyPrimitive(this.node.data);
     this.isList = this.isEventPropertyList(this.node.data);
     this.isNested = this.isEventPropertyNested(this.node.data);
     this.timestampProperty = this.isTimestampProperty(this.node.data);
 
-    if (this.isPrimitive) {
-      this.runtimeType = this.parseType(this.node.data.runtimeType);
+    if (this.node.data instanceof EventProperty) {
+      this.originalRuntimeName = this.findOriginalProperty().runtimeName;
+      this.showFieldStatus = this.fieldStatusInfo && this.fieldStatusInfo[this.originalRuntimeName] !== undefined;
+      this.showEventPreview = this.eventPreview && this.eventPreview.length > 0 && this.eventPreview[0][this.originalRuntimeName] !== undefined;
+      if (this.isPrimitive) {
+        this.originalRuntimeType = this.parseType(this.findOriginalProperty().runtimeType);
+        this.runtimeType = this.parseType((this.node.data as EventPropertyPrimitive).runtimeType);
+      }
     }
 
     if (!this.node.data.propertyScope) {
@@ -86,7 +94,9 @@ export class EventPropertyRowComponent implements OnInit, OnChanges {
     }
   }
 
-  ngOnChanges(changes: SimpleChanges): void {
+  private findOriginalProperty(): any {
+    return this.originalEventSchema.eventProperties
+      .find(ep => ep.elementId === this.node.data.elementId);
   }
 
   private parseType(runtimeType: string) {
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.html b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.html
index 79bf5a41d..d3cad53f6 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.html
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.html
@@ -16,4 +16,26 @@
   ~
   -->
 
-<pre>{{ eventSchema | json }}</pre>
\ No newline at end of file
+<div fxFlex="100" fxLayout="row" fxLayoutGap="15px">
+    <div fxFlex="50" fxLayout="column">
+        <sp-basic-inner-panel [showTitle]="true" panelTitle="Original (Parsed)">
+            <pre [innerHTML]="originalField | jsonpretty" class="preview-text"></pre>
+        </sp-basic-inner-panel>
+    </div>
+
+    <div fxFlex="50" fxLayout="column">
+        <sp-basic-inner-panel [showTitle]="true" panelTitle="Result" fxFlex="100">
+            <div header fxLayoutAlign="end center" fxFlex="100">
+                <button color="accent"
+                        mat-button
+                        data-cy="connect-schema-update-preview-btn"
+                        matTooltip="Update event preview"
+                        (click)="updateEventPreview()">
+                    <mat-icon>refresh</mat-icon>
+                    <span>&nbsp;Update result preview</span>
+                </button>
+            </div>
+            <pre [innerHTML]="desiredField | jsonpretty" class="preview-text"></pre>
+        </sp-basic-inner-panel>
+    </div>
+</div>
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss
index 58ba04bdd..eddad5e07 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss
@@ -16,3 +16,14 @@
  *
  */
 
+.preview-text {
+  background-color: black;
+  font: 9pt Inconsolata, monospace;
+  text-shadow: 0 0 5px #C8C8C8;
+  color: white;
+  padding: 10px;
+  max-width: 100%;
+  max-height: 300px;
+  overflow-y: scroll;
+  white-space: pre-wrap;
+}
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts
index f80de67d9..11823a586 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.ts
@@ -16,14 +16,43 @@
  *
  */
 
-import { Component, Input } from '@angular/core';
-import { EventSchema } from '@streampipes/platform-services';
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { EventSchema, GuessTypeInfo } from '@streampipes/platform-services';
 
 @Component({
-    selector: 'sp-event-schema-preview',
-    templateUrl: './event-schema-preview.component.html',
-    styleUrls: ['./event-schema-preview.component.scss']
+  selector: 'sp-event-schema-preview',
+  templateUrl: './event-schema-preview.component.html',
+  styleUrls: ['./event-schema-preview.component.scss']
 })
-export class EventSchemaPreviewComponent {
-    @Input() eventSchema: EventSchema;
+export class EventSchemaPreviewComponent implements OnInit {
+
+  @Input() originalEventSchema: EventSchema;
+  @Input() desiredEventSchema: EventSchema;
+
+  @Input() originalPreview: Record<string, GuessTypeInfo>;
+  @Input() desiredPreview: Record<string, GuessTypeInfo>;
+
+  @Output() updatePreviewEmitter = new EventEmitter();
+
+  originalField: Record<string, any>;
+  desiredField: Record<string, any>;
+
+  ngOnInit(): void {
+    this.originalField = this.toSimpleMap(this.originalPreview);
+    this.desiredField = this.toSimpleMap(this.desiredPreview);
+  }
+
+  toSimpleMap(event: Record<string, GuessTypeInfo>): Record<string, any> {
+    const result = {};
+    for (const key in event) {
+      result[key] = event[key].value;
+    }
+
+
+    return result;
+  }
+
+  public updateEventPreview() {
+    this.updatePreviewEmitter.emit();
+  }
 }
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
index f7474a0e7..6709dad75 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.html
@@ -17,12 +17,13 @@
   -->
 
 
-<div fxLayout="row" fxLayoutAlign="center" class="mt-20">
+<div fxLayout="column" fxLayoutAlign="center" class="mt-20">
     <div fxFlex="90" fxLayout="column">
         <div fxLayout="column"
              fxFlex="100"
              fxLayoutAlign="start center"
-            *ngIf="!isLoading && !isError && schemaErrorHints.length === 0" class="schema-validation schema-validation-ok">
+             *ngIf="!isLoading && !isError && schemaErrorHints.length === 0"
+             class="schema-validation schema-validation-ok">
             <div fxFlex="100"
                  fxLayout="row"
                  fxLayoutAlign="start center"
@@ -36,7 +37,8 @@
                  fxLayout="column"
                  [ngClass]="schemaErrorHint.level === 'error' ? 'schema-validation schema-validation-error' : 'schema-validation schema-validation-warning'"
                  fxLayoutAlign="start center" *ngFor="let schemaErrorHint of schemaErrorHints">
-                <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start" [ngClass]="schemaErrorHint.level === 'error' ? 'schema-validation-text-error' : 'schema-validation-text-warning'">
+                <div fxFlex="100" fxLayout="row" fxLayoutAlign="center start"
+                     [ngClass]="schemaErrorHint.level === 'error' ? 'schema-validation-text-error' : 'schema-validation-text-warning'">
                     <i class="material-icons">warning</i>&nbsp;
                     <b>{{schemaErrorHint.title}}</b>
                 </div>
@@ -53,7 +55,7 @@
                                          (addStaticValuePropertyEmitter)="addStaticValueProperty()"
                                          (addTimestampPropertyEmitter)="addTimestampProperty()"
                                          (guessSchemaEmitter)="guessSchema()"
-                                         (togglePreviewEmitter)="togglePreview()"
+                                         (updatePreviewEmitter)="updatePreview()"
                                          (removeSelectedPropertiesEmitter)="removeSelectedProperties()">
                 </sp-schema-editor-header>
             </div>
@@ -78,7 +80,7 @@
                         *ngIf="isError && !isLoading">
                 </sp-error-message>
 
-                <div *ngIf="!isError && !isLoading && eventSchema"
+                <div *ngIf="!isError && !isLoading && eventSchema && oldEventSchema && nodes"
                      fxLayout="column"
                      fxLayoutAlign="space-evenly stretched"
                      class="drag-drop-tree"
@@ -92,6 +94,7 @@
                                     [node]="node"
                                     [(isEditable)]="isEditable"
                                     [(eventSchema)]="eventSchema"
+                                    [(originalEventSchema)]="oldEventSchema"
                                     [eventPreview]="eventPreview"
                                     [fieldStatusInfo]="fieldStatusInfo"
                                     (refreshTreeEmitter)="refreshTree()"
@@ -102,13 +105,17 @@
             </div>
         </sp-basic-inner-panel>
     </div>
-    <div fxFlex="0 1 50%" *ngIf="isPreviewEnabled">
-        <sp-event-schema-preview [eventSchema]="eventSchema"></sp-event-schema-preview>
+    <div fxFlex="100" *ngIf="desiredPreview && isPreviewEnabled && !isLoading && !isError">
+        <sp-event-schema-preview [originalEventSchema]="oldEventSchema"
+                                 [desiredEventSchema]="eventSchema"
+                                 [originalPreview]="eventPreview[0]"
+                                 [desiredPreview]="desiredPreview"
+                                 (updatePreviewEmitter)="updatePreview()"></sp-event-schema-preview>
     </div>
 </div>
 
 
-<div fxLayoutAlign="end">
+<div fxLayoutAlign="end" class="mt-10">
     <button class="mat-basic" mat-raised-button (click)="removeSelection()">Cancel</button>
     <button class="mat-basic stepper-button" mat-raised-button (click)="goBack()">Back</button>
     <button class="stepper-button"
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
index d6e19e214..e7867d91f 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/event-schema/event-schema.component.ts
@@ -36,6 +36,7 @@ import {
   FieldStatusInfo, GuessTypeInfo,
   StreamPipesErrorMessage
 } from '../../../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
+import { TransformationRuleService } from '../../../../services/transformation-rule.service';
 
 @Component({
   selector: 'sp-event-schema',
@@ -44,7 +45,9 @@ import {
 })
 export class EventSchemaComponent implements OnChanges {
 
-  constructor(private restService: RestService, private dataTypesService: DataTypesService) {
+  constructor(private restService: RestService,
+              private dataTypesService: DataTypesService,
+              private transformationRuleService: TransformationRuleService ) {
   }
 
   @Input() adapterDescription: AdapterDescription;
@@ -83,6 +86,7 @@ export class EventSchemaComponent implements OnChanges {
   schemaErrorHints: UserErrorMessage[] = [];
 
   eventPreview: Record<string, GuessTypeInfo>[];
+  desiredPreview: Record<string, GuessTypeInfo>;
   fieldStatusInfo: Record<string, FieldStatusInfo>;
 
   options: ITreeOptions = {
@@ -124,6 +128,10 @@ export class EventSchemaComponent implements OnChanges {
         this.isEditable = true;
         this.isEditableChange.emit(true);
         this.isLoading = false;
+
+        if (guessSchema.eventPreview) {
+          this.updatePreview();
+        }
       },
       errorMessage => {
         this.errorMessage = errorMessage.error;
@@ -138,7 +146,6 @@ export class EventSchemaComponent implements OnChanges {
     this.nodes = new Array<EventProperty>();
     this.nodes.push(this.eventSchema as unknown as EventProperty);
     this.validEventSchema = this.checkIfValid(this.eventSchema);
-    // this.tree.treeModel.update();
   }
 
   public addNestedProperty(eventProperty?: EventPropertyNested): void {
@@ -201,8 +208,15 @@ export class EventSchemaComponent implements OnChanges {
     this.refreshTree();
   }
 
-  public togglePreview(): void {
-    this.isPreviewEnabled = !this.isPreviewEnabled;
+  public updatePreview(): void {
+    this.isPreviewEnabled = false;
+    this.transformationRuleService.setOldEventSchema(this.oldEventSchema);
+    this.transformationRuleService.setNewEventSchema(this.eventSchema);
+    const ruleDescriptions = this.transformationRuleService.getTransformationRuleDescriptions();
+    this.restService.getAdapterEventPreview({rules: ruleDescriptions, inputData: this.eventPreview[0]}).subscribe(preview => {
+      this.desiredPreview = preview;
+      this.isPreviewEnabled = true;
+    });
   }
 
   ngOnChanges(changes: SimpleChanges) {
@@ -238,9 +252,17 @@ export class EventSchemaComponent implements OnChanges {
       this.schemaErrorHints.push(new UserErrorMessage('Missing Timestamp', 'The timestamp must be a UNIX timestamp in milliseconds. Edit the timestamp field or add an ingestion timestamp.'));
     }
 
-    const badFields = eventSchema.eventProperties.map(ep => this.fieldStatusInfo[ep.runtimeName]).find(field => field.fieldStatus !== 'GOOD');
-    if (badFields !== undefined) {
-      this.schemaErrorHints.push(new UserErrorMessage('Bad reading', 'At least one field could not be properly read. If this is a permanent problem, consider removing it - keeping this field might cause the adapter to fail or to omit sending events.', 'warning'));
+    if (this.fieldStatusInfo) {
+      const badFields = eventSchema.eventProperties
+        .filter(ep => this.fieldStatusInfo[ep.runtimeName] !== undefined)
+        .map(ep => this.fieldStatusInfo[ep.runtimeName])
+        .find(field => field.fieldStatus !== 'GOOD');
+      if (badFields !== undefined) {
+        this.schemaErrorHints.push(new UserErrorMessage(
+          'Bad reading',
+          'At least one field could not be properly read. If this is a permanent problem, consider removing it - keeping this field might cause the adapter to fail or to omit sending events.',
+          'warning'));
+      }
     }
 
     return hasTimestamp;
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/schema-editor-header/schema-editor-header.component.ts b/ui/src/app/connect/components/new-adapter/schema-editor/schema-editor-header/schema-editor-header.component.ts
index 27db921bc..7bcd8cc3c 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/schema-editor-header/schema-editor-header.component.ts
+++ b/ui/src/app/connect/components/new-adapter/schema-editor/schema-editor-header/schema-editor-header.component.ts
@@ -33,7 +33,7 @@ export class SchemaEditorHeaderComponent implements OnInit {
   @Output() addStaticValuePropertyEmitter = new EventEmitter();
   @Output() addTimestampPropertyEmitter = new EventEmitter();
   @Output() guessSchemaEmitter = new EventEmitter();
-  @Output() togglePreviewEmitter = new EventEmitter();
+  @Output() updatePreviewEmitter = new EventEmitter();
   @Output() removeSelectedPropertiesEmitter = new EventEmitter();
 
   constructor() { }
@@ -57,10 +57,6 @@ export class SchemaEditorHeaderComponent implements OnInit {
     this.guessSchemaEmitter.emit();
   }
 
-  public togglePreview() {
-    this.togglePreviewEmitter.emit();
-  }
-
   public removeSelectedProperties() {
     this.removeSelectedPropertiesEmitter.emit();
   }
diff --git a/ui/src/app/connect/connect.module.ts b/ui/src/app/connect/connect.module.ts
index 1b23c969c..1bd77b851 100644
--- a/ui/src/app/connect/connect.module.ts
+++ b/ui/src/app/connect/connect.module.ts
@@ -84,6 +84,7 @@ import { EditValueTransformationComponent } from './dialog/edit-event-property/c
 import { SpEpSettingsSectionComponent } from './dialog/edit-event-property/components/ep-settings-section/ep-settings-section.component';
 import { SpAdapterOptionsPanelComponent } from './components/new-adapter/start-adapter-configuration/adapter-options-panel/adapter-options-panel.component';
 import { SpAdapterTemplateDialogComponent } from './dialog/adapter-template/adapter-template-dialog.component';
+import { JsonPrettyPrintPipe } from './filter/json-pretty-print.pipe';
 
 @NgModule({
   imports: [
@@ -146,6 +147,7 @@ import { SpAdapterTemplateDialogComponent } from './dialog/adapter-template/adap
     AdapterFilterPipe,
     FormatItemComponent,
     FormatListComponent,
+    JsonPrettyPrintPipe,
     NewAdapterComponent,
     SpAdapterTemplateDialogComponent,
     PipelineElementRuntimeInfoComponent,
diff --git a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss b/ui/src/app/connect/filter/json-pretty-print.pipe.ts
similarity index 70%
copy from ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss
copy to ui/src/app/connect/filter/json-pretty-print.pipe.ts
index 58ba04bdd..5383916e4 100644
--- a/ui/src/app/connect/components/new-adapter/schema-editor/event-schema-preview/event-schema-preview.component.scss
+++ b/ui/src/app/connect/filter/json-pretty-print.pipe.ts
@@ -16,3 +16,18 @@
  *
  */
 
+import { Injectable, Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+  name: 'jsonpretty'
+})
+@Injectable({providedIn: 'root'})
+export class JsonPrettyPrintPipe implements PipeTransform {
+
+  transform(json) {
+    return JSON.stringify(json, undefined, 4)
+      .replace(/ /g, '&nbsp;')
+      .replace(/\n/g, '<br/>');
+  }
+
+}
diff --git a/ui/src/app/connect/services/rest.service.ts b/ui/src/app/connect/services/rest.service.ts
index 5d33cdc3c..17459d539 100644
--- a/ui/src/app/connect/services/rest.service.ts
+++ b/ui/src/app/connect/services/rest.service.ts
@@ -23,16 +23,20 @@ import { HttpClient } from '@angular/common/http';
 import { from, Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { UnitDescription } from '../model/UnitDescription';
-import { AdapterDescription, FormatDescription, GuessSchema, Message, SpDataStream, PlatformServicesCommons } from '@streampipes/platform-services';
+import {
+  AdapterDescription, FormatDescription, GuessSchema, Message, SpDataStream, PlatformServicesCommons,
+  AdapterEventPreview,
+  GuessTypeInfo
+} from '@streampipes/platform-services';
 import { AuthService } from '../../services/auth.service';
 
 @Injectable()
 export class RestService {
 
   constructor(
-      private http: HttpClient,
-      private platformServicesCommons: PlatformServicesCommons,
-      private authService: AuthService) {
+    private http: HttpClient,
+    private platformServicesCommons: PlatformServicesCommons,
+    private authService: AuthService) {
   }
 
   get connectPath() {
@@ -66,7 +70,11 @@ export class RestService {
       .pipe(map(response => {
         return GuessSchema.fromData(response as GuessSchema);
       }));
+  }
 
+  getAdapterEventPreview(adapterEventPreview: AdapterEventPreview): Observable<Record<string, GuessTypeInfo>> {
+    return this.http.post(`${this.connectPath}/master/guess/schema/preview`, adapterEventPreview)
+      .pipe(map(response => response as Record<string, GuessTypeInfo>));
   }
 
   getSourceDetails(sourceElementId): Observable<SpDataStream> {
@@ -78,7 +86,7 @@ export class RestService {
 
   getRuntimeInfo(sourceDescription): Observable<any> {
     return this.http.post(`${this.platformServicesCommons.apiBasePath}/pipeline-element/runtime`, sourceDescription, {
-      headers: { ignoreLoadingBar: '' }
+      headers: {ignoreLoadingBar: ''}
     });
   }