You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by si...@apache.org on 2019/04/03 22:41:38 UTC

[sling-whiteboard] branch master updated: [feature-diff] initial FeatureDiffJSONSerializer + related test implementation

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

simonetripodi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 463d827  [feature-diff] initial FeatureDiffJSONSerializer + related test implementation
463d827 is described below

commit 463d8275574844e6c00f5ef936cbf20d5e144912
Author: stripodi <st...@192.168.1.111>
AuthorDate: Thu Apr 4 00:41:31 2019 +0200

    [feature-diff] initial FeatureDiffJSONSerializer + related test
    implementation
---
 feature-diff/pom.xml                               |  14 +-
 .../org/apache/sling/feature/diff/DiffSection.java |  16 +++
 .../diff/io/json/FeatureDiffJSONSerializer.java    | 147 +++++++++++++++++++++
 .../sling/feature/diff/io/json/package-info.java   |  21 +++
 .../io/json/FeatureDiffJSONSerializerTest.java     | 105 +++++++++++++++
 .../sling/feature/diff/io/json/expectedDiff.json   |  70 ++++++++++
 6 files changed, 370 insertions(+), 3 deletions(-)

diff --git a/feature-diff/pom.xml b/feature-diff/pom.xml
index 85019f5..4187689 100644
--- a/feature-diff/pom.xml
+++ b/feature-diff/pom.xml
@@ -69,6 +69,14 @@
       <scope>provided</scope>
     </dependency>
 
+    <!-- Serialize the JSON FeatureDiff -->
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-json_1.0_spec</artifactId>
+      <version>1.0-alpha-1</version>
+      <scope>provided</scope>
+    </dependency>
+
     <!--
      | Test only dependencies
     -->
@@ -78,9 +86,9 @@
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <version>2.25.0</version>
+      <groupId>org.apache.johnzon</groupId>
+      <artifactId>johnzon-core</artifactId>
+      <version>1.0.0</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java
index 8ccc5db..a62aa51 100644
--- a/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/DiffSection.java
@@ -78,6 +78,22 @@ public final class DiffSection {
         return updates;
     }
 
+    public boolean hasAdded() {
+        return !added.isEmpty();
+    }
+
+    public boolean hasRemoved() {
+        return !removed.isEmpty();
+    }
+
+    public boolean hasUpdatedItems() {
+        return !updatedItems.isEmpty();
+    }
+
+    public boolean hasUpdates() {
+        return !updates.isEmpty();
+    }
+
     public boolean isEmpty() {
         return added.isEmpty()
                 && removed.isEmpty()
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java
new file mode 100644
index 0000000..df2da47
--- /dev/null
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializer.java
@@ -0,0 +1,147 @@
+/*
+ * 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.sling.feature.diff.io.json;
+
+import static org.apache.commons.lang3.builder.ToStringStyle.NO_CLASS_NAME_STYLE;
+import static java.util.Objects.requireNonNull;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+
+import javax.json.Json;
+import javax.json.JsonValue;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.sling.feature.diff.DiffSection;
+import org.apache.sling.feature.diff.FeatureDiff;
+import org.apache.sling.feature.diff.UpdatedItem;
+
+public final class FeatureDiffJSONSerializer {
+
+    private static final JsonGeneratorFactory GENERATOR_FACTORY = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
+
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss Z");
+
+    public static void serializeFeatureDiff(FeatureDiff featureDiff, OutputStream output) {
+        requireNonNull(output, "Impossible to serialize Feature Diff to a null stream");
+        serializeFeatureDiff(featureDiff, new OutputStreamWriter(output));
+    }
+
+    public static void serializeFeatureDiff(FeatureDiff featureDiff, Writer writer) {
+        requireNonNull(featureDiff, "Impossible to serialize null Feature Diff");
+        requireNonNull(writer, "Impossible to serialize Feature Diff to a null stream");
+
+        JsonGenerator generator = GENERATOR_FACTORY.createGenerator(writer);
+        generator.writeStartObject();
+
+        generator.write("vendor", "The Apache Software Foundation");
+        generator.write("vendorURL", "http://www.apache.org/");
+        generator.write("generator", "Apache Sling Feature Diff tool");
+        generator.write("generatorURL", "https://github.com/apache/sling-org-apache-sling-feature-diff");
+        generator.write("generatedOn", DATE_FORMAT.format(new Date()));
+        generator.write("id", featureDiff.getCurrent().getId().toMvnId());
+        generator.write("previousVersion", featureDiff.getPrevious().getId().getVersion());
+
+        for (DiffSection diffSection : featureDiff.getSections()) {
+            serializeDiffSection(diffSection, true, generator);
+        }
+
+        generator.writeEnd().close();
+    }
+
+    private static void serializeDiffSection(DiffSection diffSection, boolean main, JsonGenerator generator) {
+        if (main) {
+            generator.writeStartObject(diffSection.getId());
+        } else {
+            generator.writeStartObject()
+                     .write("id", diffSection.getId());
+        }
+
+        if (diffSection.hasRemoved()) {
+            writeArray("removed", diffSection.getRemoved(), generator);
+        }
+
+        if (diffSection.hasAdded()) {
+            writeArray("added", diffSection.getAdded(), generator);
+        }
+
+        if (diffSection.hasUpdatedItems() || diffSection.hasUpdates()) {
+            generator.writeStartArray("updated");
+
+            for (UpdatedItem<?> updatedItem : diffSection.getUpdatedItems()) {
+                generator.writeStartObject().write("id", updatedItem.getId());
+                writeValue("previous", updatedItem.getPrevious(), generator);
+                writeValue("current", updatedItem.getCurrent(), generator);
+                generator.writeEnd();
+            }
+
+            for (DiffSection updatesDiffSection : diffSection.getUpdates()) {
+                serializeDiffSection(updatesDiffSection, false, generator);
+            }
+
+            generator.writeEnd();
+        }
+
+        generator.writeEnd();
+    }
+
+    private static void writeArray(String name, Iterable<String> values, JsonGenerator generator) {
+        generator.writeStartArray(name);
+
+        for (String value : values) {
+            generator.write(value);
+        }
+
+        generator.writeEnd();
+    }
+
+    // TODO find a faster and more elegant implementation
+    private static <T> void writeValue(String key, T value, JsonGenerator generator) {
+        if (value == null) {
+            generator.write(key, JsonValue.NULL);
+        } else if (value instanceof Boolean) {
+            generator.write(key, ((Boolean) value).booleanValue());
+        } else if (value instanceof BigDecimal) {
+            generator.write(key, (BigDecimal) value);
+        } else if (value instanceof BigInteger) {
+            generator.write(key, (BigInteger) value);
+        } else if (value instanceof Integer) {
+            generator.write(key, (Integer) value);
+        } else if (value instanceof Long) {
+            generator.write(key, (Long) value);
+        } else if (value instanceof Double) {
+            generator.write(key, (Double) value);
+        } else if (value instanceof String) {
+            generator.write(key, (String) value);
+        } else {
+            generator.write(key, ReflectionToStringBuilder.toString(value, NO_CLASS_NAME_STYLE));
+        }
+    }
+
+    private FeatureDiffJSONSerializer() {
+        // this class can not be instantiated
+    }
+
+}
diff --git a/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java b/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java
new file mode 100644
index 0000000..f74f569
--- /dev/null
+++ b/feature-diff/src/main/java/org/apache/sling/feature/diff/io/json/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * JSON streaming serializer to represent the Feature diff result.
+ */
+package org.apache.sling.feature.diff.io.json;
diff --git a/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java b/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java
new file mode 100644
index 0000000..5153efa
--- /dev/null
+++ b/feature-diff/src/test/java/org/apache/sling/feature/diff/io/json/FeatureDiffJSONSerializerTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.sling.feature.diff.io.json;
+
+import static org.apache.sling.feature.diff.FeatureDiff.compareFeatures;
+import static org.junit.Assert.assertEquals;
+
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.diff.FeatureDiff;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.flipkart.zjsonpatch.JsonDiff;
+
+public class FeatureDiffJSONSerializerTest {
+
+    @Test(expected = NullPointerException.class)
+    public void nullOutputStreamNotAccepted() {
+        FeatureDiffJSONSerializer.serializeFeatureDiff(null, (OutputStream) null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void nullFeatureDiffNotAccepted() {
+        FeatureDiffJSONSerializer.serializeFeatureDiff(null, (Writer) null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void nullWriterNotAccepted() {
+        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
+        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
+        FeatureDiff featureDiff = compareFeatures(previous, current);
+
+        FeatureDiffJSONSerializer.serializeFeatureDiff(featureDiff , (Writer) null);
+    }
+
+    @Test
+    public void serialization() throws Exception {
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        // the expected result
+
+        JsonNode expectedNode = objectMapper.readTree(getClass().getResourceAsStream("expectedDiff.json"));
+
+        // produce the JSON output
+
+        Feature previous = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:0.9.0"));
+        previous.getFrameworkProperties().put("env", "staging");
+        previous.getFrameworkProperties().put("sling.framework.install.incremental", "true");
+        previous.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:removed:1.0.0")));
+        previous.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:updated:1.0.0")));
+        previous.getConfigurations().add(new Configuration("org.apache.sling.feature.diff.config.removed"));
+
+        Configuration previousConfiguration = new Configuration("org.apache.sling.feature.diff.config.updated");
+        previousConfiguration.getProperties().put("it.will.appear.in.the.removed.section", 123);
+        previousConfiguration.getProperties().put("it.will.appear.in.the.updated.section", new String[] { "/log" });
+        previous.getConfigurations().add(previousConfiguration);
+
+        Feature current = new Feature(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:1.0.0"));
+        current.getFrameworkProperties().put("env", "prod");
+        current.getFrameworkProperties().put("sling.framework.install.startlevel", "1");
+        current.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:added:1.0.0")));
+        current.getBundles().add(new Artifact(ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.feature.diff:updated:2.0.0")));
+        current.getConfigurations().add(new Configuration("org.apache.sling.feature.diff.config.added"));
+
+        Configuration currentConfiguration = new Configuration("org.apache.sling.feature.diff.config.updated");
+        currentConfiguration.getProperties().put("it.will.appear.in.the.updated.section", new String[] { "/log", "/etc" });
+        currentConfiguration.getProperties().put("it.will.appear.in.the.added.section", true);
+        current.getConfigurations().add(currentConfiguration);
+
+        FeatureDiff featureDiff = compareFeatures(previous, current);
+
+        StringWriter stringWriter = new StringWriter();
+        FeatureDiffJSONSerializer.serializeFeatureDiff(featureDiff , stringWriter);
+
+        JsonNode actualNode = objectMapper.readTree(stringWriter.toString());
+
+        // assert the differences
+        JsonNode patchNode = JsonDiff.asJson(expectedNode, actualNode);
+        // expected 1 node as diff, i.e. [{"op":"replace","path":"/generatedOn","value":"2019-04-04T12:38:46 +0200"}]
+        assertEquals(patchNode.toString(), 1, patchNode.size());
+    }
+
+}
diff --git a/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json b/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json
new file mode 100644
index 0000000..7b1b55a
--- /dev/null
+++ b/feature-diff/src/test/resources/org/apache/sling/feature/diff/io/json/expectedDiff.json
@@ -0,0 +1,70 @@
+{
+  "vendor":"The Apache Software Foundation",
+  "vendorURL":"http://www.apache.org/",
+  "generator":"Apache Sling Feature Diff tool",
+  "generatorURL":"https://github.com/apache/sling-org-apache-sling-feature-diff",
+  "generatedOn":"2019-04-04T12:24:25 +0200",
+  "id":"org.apache.sling:org.apache.sling.feature.diff:1.0.0",
+  "previousVersion":"0.9.0",
+  "framework-properties":{
+    "removed":[
+      "sling.framework.install.incremental"
+    ],
+    "added":[
+      "sling.framework.install.startlevel"
+    ],
+    "updated":[
+      {
+        "id":"env",
+        "previous":"staging",
+        "current":"prod"
+      }
+    ]
+  },
+  "bundles":{
+    "removed":[
+      "org.apache.sling:org.apache.sling.feature.diff:removed:1.0.0"
+    ],
+    "added":[
+      "org.apache.sling:org.apache.sling.feature.diff:added:1.0.0"
+    ],
+    "updated":[
+      {
+        "id":"org.apache.sling:org.apache.sling.feature.diff:updated:2.0.0",
+        "updated":[
+          {
+            "id":"version",
+            "previous":"1.0.0",
+            "current":"2.0.0"
+          }
+        ]
+      }
+    ]
+  },
+  "configurations":{
+    "removed":[
+      "org.apache.sling.feature.diff.config.removed"
+    ],
+    "added":[
+      "org.apache.sling.feature.diff.config.added"
+    ],
+    "updated":[
+      {
+        "id":"org.apache.sling.feature.diff.config.updated",
+        "removed":[
+          "it.will.appear.in.the.removed.section"
+        ],
+        "added":[
+          "it.will.appear.in.the.added.section"
+        ],
+        "updated":[
+          {
+            "id":"it.will.appear.in.the.updated.section",
+            "previous":"[{/log}]",
+            "current":"[{/log,/etc}]"
+          }
+        ]
+      }
+    ]
+  }
+}