From 5362a80afea4bc54b3c040225aa4c5588e003888 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 23 Mar 2020 10:06:47 +0100 Subject: [PATCH 01/21] Proposal to refactor the Event class Signed-off-by: Francesco Guardiani --- .../main/java/io/cloudevents/Attributes.java | 6 ++--- api/src/main/java/io/cloudevents/Builder.java | 11 ++++---- .../main/java/io/cloudevents/CloudEvent.java | 10 +++---- api/src/main/java/io/cloudevents/Data.java | 26 +++++++++++++++++++ .../main/java/io/cloudevents/SpecVersion.java | 7 +++++ 5 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 api/src/main/java/io/cloudevents/Data.java create mode 100644 api/src/main/java/io/cloudevents/SpecVersion.java diff --git a/api/src/main/java/io/cloudevents/Attributes.java b/api/src/main/java/io/cloudevents/Attributes.java index 5a5f8d4b1..1a004cb35 100644 --- a/api/src/main/java/io/cloudevents/Attributes.java +++ b/api/src/main/java/io/cloudevents/Attributes.java @@ -25,7 +25,7 @@ /** * The marker interface for CloudEvents attributes - * + * * @author fabiojose * */ @@ -53,7 +53,7 @@ public interface Attributes { * @return The version of the CloudEvents specification which the event uses */ @NotBlank - String getSpecversion(); + SpecVersion getSpecversion(); /** * A common way to get the media type of CloudEvents 'data'; @@ -61,5 +61,5 @@ public interface Attributes { */ @JsonIgnore Optional getMediaType(); - + } diff --git a/api/src/main/java/io/cloudevents/Builder.java b/api/src/main/java/io/cloudevents/Builder.java index 419b7cc43..7b4971440 100644 --- a/api/src/main/java/io/cloudevents/Builder.java +++ b/api/src/main/java/io/cloudevents/Builder.java @@ -16,24 +16,23 @@ package io.cloudevents; /** - * + * * @author fabiojose * */ -public interface Builder { +public interface Builder { /** * To build a brand new instance of {@link CloudEvent} */ - CloudEvent build(); - + CloudEvent build(); + /** * To build a brand new instance of {@link CloudEvent} with another * type of 'data' - * @param The new type of 'data' * @param id The new id for the new instance * @param base The base {@link CloudEvent} to copy its attributes * @param newData The new 'data' */ - CloudEvent build(CloudEvent base, String id, TT newData); + CloudEvent build(CloudEvent base, String id, Data newData); } diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index ece497394..68252a676 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -20,23 +20,19 @@ /** * An abstract event envelope - * @param The attributes type - * @param The 'data' type * @author fabiojose */ -public interface CloudEvent { +public interface CloudEvent { /** * The event context attributes */ - A getAttributes(); + Attributes getAttributes(); /** * The event data */ - Optional getData(); - - byte[] getDataBase64(); + Optional getData(); /** * The event extensions diff --git a/api/src/main/java/io/cloudevents/Data.java b/api/src/main/java/io/cloudevents/Data.java new file mode 100644 index 000000000..c88273db9 --- /dev/null +++ b/api/src/main/java/io/cloudevents/Data.java @@ -0,0 +1,26 @@ +package io.cloudevents; + +import com.fasterxml.jackson.databind.JsonNode; + +public interface Data { + + byte[] asBinary(); + + JsonNode asJson(); + + static Data newJson(JsonNode json) { + //TODO + return null; + } + + static Data newJson(Object json) { + //TODO + return null; + } + + static Data newBinary(byte[] binary) { + //TODO + return null; + } + +} diff --git a/api/src/main/java/io/cloudevents/SpecVersion.java b/api/src/main/java/io/cloudevents/SpecVersion.java new file mode 100644 index 000000000..0ec33dce1 --- /dev/null +++ b/api/src/main/java/io/cloudevents/SpecVersion.java @@ -0,0 +1,7 @@ +package io.cloudevents; + +public enum SpecVersion { + V02, + V03, + V1 +} From 670d1c13f4e41c4606bff1df3d84ad88fd5c9d31 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Thu, 16 Apr 2020 11:41:49 +0200 Subject: [PATCH 02/21] Some steps forward Signed-off-by: Francesco Guardiani --- api/pom.xml | 40 +- .../main/java/io/cloudevents/Attributes.java | 30 +- api/src/main/java/io/cloudevents/Builder.java | 38 -- .../main/java/io/cloudevents/CloudEvent.java | 25 ++ api/src/main/java/io/cloudevents/Data.java | 35 +- .../cloudevents/DataConversionException.java | 8 + .../main/java/io/cloudevents/Extension.java | 9 + .../main/java/io/cloudevents/SpecVersion.java | 1 - .../common/BaseCloudEventBuilder.java | 71 ++++ .../io/cloudevents/common/BinaryData.java | 58 +++ .../io/cloudevents/common/CloudEventImpl.java | 39 ++ .../java/io/cloudevents/common/JsonData.java | 61 +++ .../io/cloudevents/common/StringData.java | 58 +++ .../DistributedTracingExtension.java | 108 ++---- .../extensions/ExtensionFormat.java | 101 ----- .../extensions/ExtensionsParser.java | 32 ++ .../extensions/InMemoryFormat.java | 63 --- .../format/BinaryUnmarshaller.java | 60 +-- .../java/io/cloudevents/fun/EventBuilder.java | 39 -- .../main/java/io/cloudevents/json/Json.java | 70 ++-- .../json/ZonedDateTimeDeserializer.java | 6 +- .../json/ZonedDateTimeSerializer.java | 4 +- .../java/io/cloudevents/v02/Accessor.java | 59 --- .../io/cloudevents/v02/AttributesImpl.java | 212 ---------- .../io/cloudevents/v02/CloudEventBuilder.java | 254 ------------ .../io/cloudevents/v02/CloudEventImpl.java | 143 ------- .../io/cloudevents/v02/ContextAttributes.java | 42 -- .../cloudevents/v02/http/AttributeMapper.java | 82 ---- .../cloudevents/v02/http/ExtensionMapper.java | 65 ---- .../io/cloudevents/v02/http/HeaderMapper.java | 78 ---- .../io/cloudevents/v02/http/Marshallers.java | 83 ---- .../cloudevents/v02/http/Unmarshallers.java | 103 ----- .../java/io/cloudevents/v03/Accessor.java | 45 --- .../io/cloudevents/v03/AttributesImpl.java | 148 ++++--- .../io/cloudevents/v03/CloudEventBuilder.java | 209 ++-------- .../io/cloudevents/v03/CloudEventImpl.java | 143 ------- .../io/cloudevents/v03/ContextAttributes.java | 25 +- .../cloudevents/v03/http/AttributeMapper.java | 18 +- .../io/cloudevents/v03/http/HeaderMapper.java | 16 +- .../main/java/io/cloudevents/v1/Accessor.java | 59 --- .../io/cloudevents/v1/AttributesImpl.java | 139 +++---- .../io/cloudevents/v1/CloudEventBuilder.java | 198 ++-------- .../io/cloudevents/v1/CloudEventImpl.java | 152 -------- .../io/cloudevents/v1/ContextAttributes.java | 23 +- .../cloudevents/v1/http/AttributeMapper.java | 18 +- .../io/cloudevents/v1/http/HeaderMapper.java | 16 +- .../java/io/cloudevents/v02/AccessorTest.java | 134 ------- .../v02/CloudEventBuilderTest.java | 361 ------------------ .../v02/CloudEventJacksonTest.java | 232 ----------- .../v02/http/AttributeMapperTest.java | 127 ------ .../v02/http/ExtensionMapperTest.java | 103 ----- .../v02/http/HTTPBinaryMarshallerTest.java | 130 ------- .../v02/http/HTTPBinaryUnmarshallerTest.java | 112 ------ .../http/HTTPStructuredMarshallerTest.java | 149 -------- .../http/HTTPStructuredUnmasharllerTest.java | 153 -------- .../v02/http/HeaderMapperTest.java | 129 ------- .../v03/CloudEventJacksonTest.java | 118 +++--- .../v03/http/HTTPBinaryUnmarshallerTest.java | 30 +- .../v03/http/HTTPStructuredUnmarshaller.java | 68 ++-- .../cloudevents/v1/CloudEventJacksonTest.java | 136 +++---- .../v1/http/HTTPBinaryUnmarshallerTest.java | 26 +- .../http/HTTPStructuredUnmarshallerTest.java | 69 ++-- .../v02/kafka/AttributeMapper.java | 76 ---- .../v02/kafka/ExtensionMapper.java | 82 ---- .../cloudevents/v02/kafka/HeaderMapper.java | 84 ---- .../io/cloudevents/v02/kafka/Marshallers.java | 82 ---- .../cloudevents/v02/kafka/Unmarshallers.java | 137 ------- .../io/cloudevents/v1/kafka/HeaderMapper.java | 4 +- .../v02/kafka/KafkaConsumerBinaryTest.java | 126 +++--- .../kafka/KafkaConsumerStructuredTest.java | 128 +++---- .../v03/kafka/KafkaConsumerBinaryTest.java | 130 +++---- .../kafka/KafkaConsumerStructuredTest.java | 126 +++--- .../v1/kafka/HeaderMapperTest.java | 65 ++-- pom.xml | 32 +- 74 files changed, 1290 insertions(+), 5145 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/Builder.java create mode 100644 api/src/main/java/io/cloudevents/DataConversionException.java create mode 100644 api/src/main/java/io/cloudevents/Extension.java create mode 100644 api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java create mode 100644 api/src/main/java/io/cloudevents/common/BinaryData.java create mode 100644 api/src/main/java/io/cloudevents/common/CloudEventImpl.java create mode 100644 api/src/main/java/io/cloudevents/common/JsonData.java create mode 100644 api/src/main/java/io/cloudevents/common/StringData.java delete mode 100644 api/src/main/java/io/cloudevents/extensions/ExtensionFormat.java create mode 100644 api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java delete mode 100644 api/src/main/java/io/cloudevents/extensions/InMemoryFormat.java delete mode 100644 api/src/main/java/io/cloudevents/fun/EventBuilder.java delete mode 100644 api/src/main/java/io/cloudevents/v02/Accessor.java delete mode 100644 api/src/main/java/io/cloudevents/v02/AttributesImpl.java delete mode 100644 api/src/main/java/io/cloudevents/v02/CloudEventBuilder.java delete mode 100644 api/src/main/java/io/cloudevents/v02/CloudEventImpl.java delete mode 100644 api/src/main/java/io/cloudevents/v02/ContextAttributes.java delete mode 100644 api/src/main/java/io/cloudevents/v02/http/AttributeMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v02/http/ExtensionMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v02/http/HeaderMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v02/http/Marshallers.java delete mode 100644 api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java delete mode 100644 api/src/main/java/io/cloudevents/v03/Accessor.java delete mode 100644 api/src/main/java/io/cloudevents/v03/CloudEventImpl.java delete mode 100644 api/src/main/java/io/cloudevents/v1/Accessor.java delete mode 100644 api/src/main/java/io/cloudevents/v1/CloudEventImpl.java delete mode 100644 api/src/test/java/io/cloudevents/v02/AccessorTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/CloudEventBuilderTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/CloudEventJacksonTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/AttributeMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/ExtensionMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/HTTPBinaryMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/HTTPBinaryUnmarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/HTTPStructuredMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/HTTPStructuredUnmasharllerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v02/http/HeaderMapperTest.java delete mode 100644 kafka/src/main/java/io/cloudevents/v02/kafka/AttributeMapper.java delete mode 100644 kafka/src/main/java/io/cloudevents/v02/kafka/ExtensionMapper.java delete mode 100644 kafka/src/main/java/io/cloudevents/v02/kafka/HeaderMapper.java delete mode 100644 kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java delete mode 100644 kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java diff --git a/api/pom.xml b/api/pom.xml index cfe7eb53f..99993a4ed 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -23,43 +23,41 @@ 1.3.0 - io.cloudevents cloudevents-api CloudEvents - API - 1.3.0 + ${parent.version} jar + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + + + com.fasterxml.jackson.core jackson-core - ${jackson.version} - com.fasterxml.jackson.core jackson-databind - ${jackson.version} + com.fasterxml.jackson.datatype jackson-datatype-jdk8 - ${jackson.version} - - - - org.hibernate.validator - hibernate-validator - ${hibernate-validator.version} - - - - org.glassfish - jakarta.el - ${jakarta.el.version} + junit junit @@ -76,10 +74,4 @@ - - 2.10.1 - 6.0.17.Final - 3.0.3 - - diff --git a/api/src/main/java/io/cloudevents/Attributes.java b/api/src/main/java/io/cloudevents/Attributes.java index 1a004cb35..5ecbc5a34 100644 --- a/api/src/main/java/io/cloudevents/Attributes.java +++ b/api/src/main/java/io/cloudevents/Attributes.java @@ -16,13 +16,9 @@ package io.cloudevents; import java.net.URI; +import java.time.ZonedDateTime; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; - -import com.fasterxml.jackson.annotation.JsonIgnore; - /** * The marker interface for CloudEvents attributes * @@ -31,35 +27,37 @@ */ public interface Attributes { + /** + * @return The version of the CloudEvents specification which the event uses + */ + SpecVersion getSpecVersion(); + /** * @return Identifies the event. Producers MUST ensure that source + id is unique for each distinct event */ - @NotBlank String getId(); /** * @return A value describing the type of event related to the originating occurrence. */ - @NotBlank String getType(); /** * @return The context in which an event happened. */ - @NotNull URI getSource(); /** - * @return The version of the CloudEvents specification which the event uses - */ - @NotBlank - SpecVersion getSpecversion(); - - /** + * TODO * A common way to get the media type of CloudEvents 'data'; * @return If has a value, it MUST follows the RFC2046 */ - @JsonIgnore - Optional getMediaType(); + Optional getDataContentType(); + + Optional getDataSchema(); + + Optional getSubject(); + + Optional getTime(); } diff --git a/api/src/main/java/io/cloudevents/Builder.java b/api/src/main/java/io/cloudevents/Builder.java deleted file mode 100644 index 7b4971440..000000000 --- a/api/src/main/java/io/cloudevents/Builder.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents; - -/** - * - * @author fabiojose - * - */ -public interface Builder { - - /** - * To build a brand new instance of {@link CloudEvent} - */ - CloudEvent build(); - - /** - * To build a brand new instance of {@link CloudEvent} with another - * type of 'data' - * @param id The new id for the new instance - * @param base The base {@link CloudEvent} to copy its attributes - * @param newData The new 'data' - */ - CloudEvent build(CloudEvent base, String id, Data newData); -} diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 68252a676..219971894 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -15,12 +15,15 @@ */ package io.cloudevents; +import io.cloudevents.v03.CloudEventBuilder; + import java.util.Map; import java.util.Optional; /** * An abstract event envelope * @author fabiojose + * @author slinkydeveloper */ public interface CloudEvent { @@ -36,6 +39,28 @@ public interface CloudEvent { /** * The event extensions + * + * Extensions values could be String/Number/Boolean/JsonNode */ Map getExtensions(); + + /** + * Write an extension into this cloud event + * @param e + */ + default void writeExtension(Extension e) { + e.writeToEvent(this); + } + + static io.cloudevents.v1.CloudEventBuilder build() { + return buildV1(); + } + + static io.cloudevents.v1.CloudEventBuilder buildV1() { + return new io.cloudevents.v1.CloudEventBuilder(); + } + + static io.cloudevents.v03.CloudEventBuilder buildV03() { + return new io.cloudevents.v03.CloudEventBuilder(); + } } diff --git a/api/src/main/java/io/cloudevents/Data.java b/api/src/main/java/io/cloudevents/Data.java index c88273db9..00b5f2c59 100644 --- a/api/src/main/java/io/cloudevents/Data.java +++ b/api/src/main/java/io/cloudevents/Data.java @@ -1,26 +1,43 @@ package io.cloudevents; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; - +import io.cloudevents.common.BinaryData; +import io.cloudevents.common.JsonData; +import io.cloudevents.common.StringData; +import io.cloudevents.json.Json; + +/** + * TODO + * + * @author slinkydeveloper + */ public interface Data { - byte[] asBinary(); + byte[] asBinary() throws DataConversionException; + + String asString() throws DataConversionException; - JsonNode asJson(); + JsonNode asJson() throws DataConversionException; static Data newJson(JsonNode json) { - //TODO - return null; + return new JsonData(json); } static Data newJson(Object json) { - //TODO - return null; + try { + return Json.MAPPER.valueToTree(json); + } catch (IllegalArgumentException e) { + throw new DataConversionException(json.getClass().toString(), "JsonNode", e); + } } static Data newBinary(byte[] binary) { - //TODO - return null; + return new BinaryData(binary); + } + + static Data newString(String string) { + return new StringData(string); } } diff --git a/api/src/main/java/io/cloudevents/DataConversionException.java b/api/src/main/java/io/cloudevents/DataConversionException.java new file mode 100644 index 000000000..a21e57fc0 --- /dev/null +++ b/api/src/main/java/io/cloudevents/DataConversionException.java @@ -0,0 +1,8 @@ +package io.cloudevents; + +public class DataConversionException extends RuntimeException { + + public DataConversionException(String from, String to, Throwable cause) { + super("Cannot convert " + from + " data to " + to, cause); + } +} diff --git a/api/src/main/java/io/cloudevents/Extension.java b/api/src/main/java/io/cloudevents/Extension.java new file mode 100644 index 000000000..f99fd0d76 --- /dev/null +++ b/api/src/main/java/io/cloudevents/Extension.java @@ -0,0 +1,9 @@ +package io.cloudevents; + +public interface Extension { + + void readFromEvent(CloudEvent event); + + void writeToEvent(CloudEvent event); + +} diff --git a/api/src/main/java/io/cloudevents/SpecVersion.java b/api/src/main/java/io/cloudevents/SpecVersion.java index 0ec33dce1..b1d1844c3 100644 --- a/api/src/main/java/io/cloudevents/SpecVersion.java +++ b/api/src/main/java/io/cloudevents/SpecVersion.java @@ -1,7 +1,6 @@ package io.cloudevents; public enum SpecVersion { - V02, V03, V1 } diff --git a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java new file mode 100644 index 000000000..ea5041fea --- /dev/null +++ b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java @@ -0,0 +1,71 @@ +package io.cloudevents.common; + +import io.cloudevents.Attributes; +import io.cloudevents.CloudEvent; +import io.cloudevents.Data; +import io.cloudevents.Extension; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public abstract class BaseCloudEventBuilder, T extends Attributes> { + + // This is a little trick for enabling fluency + private B self; + + private Data data; + private Map extensions; + private List materializedExtensions; + + + @SuppressWarnings("unchecked") + public BaseCloudEventBuilder() { + this.self = (B)this; + this.extensions = new HashMap<>(); + this.materializedExtensions = new ArrayList<>(); + } + + protected abstract B withDataContentType(String contentType); + + protected abstract B withDataSchema(URI dataSchema); + + protected abstract T buildAttributes(); + + public B withData(String contentType, Data data) { + withDataContentType(contentType); + this.data = data; + return self; + } + + public B withData(String contentType, URI dataSchema, Data data) { + withDataContentType(contentType); + withDataSchema(dataSchema); + this.data = data; + return self; + } + + public B withExtension(String key, Object value) { + this.extensions.put(key, value); + return self; + } + + public B withExtension(Extension extension) { + this.materializedExtensions.add(extension); + return self; + } + + public CloudEvent build() { + CloudEvent event = new CloudEventImpl(this.buildAttributes(), data, extensions); + + for (Extension ext : this.materializedExtensions) { + ext.writeToEvent(event); + } + + return event; + } + +} diff --git a/api/src/main/java/io/cloudevents/common/BinaryData.java b/api/src/main/java/io/cloudevents/common/BinaryData.java new file mode 100644 index 000000000..6714588b9 --- /dev/null +++ b/api/src/main/java/io/cloudevents/common/BinaryData.java @@ -0,0 +1,58 @@ +package io.cloudevents.common; + +import com.fasterxml.jackson.databind.JsonNode; +import io.cloudevents.Data; +import io.cloudevents.DataConversionException; +import io.cloudevents.json.Json; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public class BinaryData implements Data { + + private byte[] data; + + public BinaryData(byte[] data) { + this.data = data; + } + + @Override + public byte[] asBinary() { + return this.data; + } + + @Override + public String asString() { + return new String(this.data, StandardCharsets.UTF_8); + } + + @Override + public JsonNode asJson() { + try { + return Json.MAPPER.readTree(data); + } catch (IOException e) { + throw new DataConversionException("[]byte", "JsonNode", e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BinaryData that = (BinaryData) o; + return Arrays.equals(data, that.data); + } + + @Override + public int hashCode() { + return Arrays.hashCode(data); + } + + @Override + public String toString() { + return "BinaryData{" + + "data=" + Arrays.toString(data) + + '}'; + } +} diff --git a/api/src/main/java/io/cloudevents/common/CloudEventImpl.java b/api/src/main/java/io/cloudevents/common/CloudEventImpl.java new file mode 100644 index 000000000..839a6b7f6 --- /dev/null +++ b/api/src/main/java/io/cloudevents/common/CloudEventImpl.java @@ -0,0 +1,39 @@ +package io.cloudevents.common; + +import io.cloudevents.Attributes; +import io.cloudevents.CloudEvent; +import io.cloudevents.Data; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class CloudEventImpl implements CloudEvent { + + private final Attributes attributes; + private final Data data; + private final Map extensions; + + public CloudEventImpl(Attributes attributes, Data data, Map extensions) { + Objects.requireNonNull(attributes); + this.attributes = attributes; + this.data = data; + this.extensions = extensions != null ? extensions : new HashMap<>(); + } + + @Override + public Attributes getAttributes() { + return attributes; + } + + @Override + public Optional getData() { + return Optional.ofNullable(data); + } + + @Override + public Map getExtensions() { + return extensions; + } +} diff --git a/api/src/main/java/io/cloudevents/common/JsonData.java b/api/src/main/java/io/cloudevents/common/JsonData.java new file mode 100644 index 000000000..98c32458e --- /dev/null +++ b/api/src/main/java/io/cloudevents/common/JsonData.java @@ -0,0 +1,61 @@ +package io.cloudevents.common; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import io.cloudevents.Data; +import io.cloudevents.DataConversionException; +import io.cloudevents.json.Json; + +import java.util.Objects; + +public class JsonData implements Data { + + private JsonNode data; + + public JsonData(JsonNode data) { + this.data = data; + } + + @Override + public byte[] asBinary() { + try { + return Json.MAPPER.writeValueAsBytes(this.data); + } catch (JsonProcessingException e) { + throw new DataConversionException("JsonNode", "[]byte", e); + } + } + + @Override + public String asString() { + try { + return Json.MAPPER.writeValueAsString(data); + } catch (JsonProcessingException e) { + throw new DataConversionException("JsonNode", "String", e); + } + } + + @Override + public JsonNode asJson() { + return this.data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + JsonData jsonData = (JsonData) o; + return Objects.equals(data, jsonData.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public String toString() { + return "JsonData{" + + "data=" + data + + '}'; + } +} diff --git a/api/src/main/java/io/cloudevents/common/StringData.java b/api/src/main/java/io/cloudevents/common/StringData.java new file mode 100644 index 000000000..f0a7d5b11 --- /dev/null +++ b/api/src/main/java/io/cloudevents/common/StringData.java @@ -0,0 +1,58 @@ +package io.cloudevents.common; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import io.cloudevents.Data; +import io.cloudevents.DataConversionException; +import io.cloudevents.json.Json; + +import java.io.IOException; +import java.util.Objects; + +public class StringData implements Data { + + private String data; + + public StringData(String data) { + this.data = data; + } + + @Override + public byte[] asBinary() { + return this.data.getBytes(); + } + + @Override + public String asString() { + return this.data; + } + + @Override + public JsonNode asJson() { + try { + return Json.MAPPER.readTree(data); + } catch (IOException e) { + throw new DataConversionException("String", "JsonNode", e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StringData that = (StringData) o; + return Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + return Objects.hash(data); + } + + @Override + public String toString() { + return "StringData{" + + "data='" + data + '\'' + + '}'; + } +} diff --git a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java index b4fdbf177..5cf520a40 100644 --- a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java +++ b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java @@ -1,16 +1,18 @@ package io.cloudevents.extensions; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.AbstractMap.SimpleEntry; +import io.cloudevents.CloudEvent; +import io.cloudevents.Extension; -public class DistributedTracingExtension { +public class DistributedTracingExtension implements Extension { + + public static final String TRACEPARENT = "traceparent"; + public static final String TRACESTATE = "tracestate"; private String traceparent; private String tracestate; + public DistributedTracingExtension() { } + public String getTraceparent() { return traceparent; } @@ -27,6 +29,28 @@ public void setTracestate(String tracestate) { this.tracestate = tracestate; } + @Override + public void readFromEvent(CloudEvent event) { + Object tp = event.getExtensions().get(TRACEPARENT); + if (tp != null) { + this.traceparent = tp.toString(); + } + Object ts = event.getExtensions().get(TRACESTATE); + if (ts != null) { + this.tracestate = ts.toString(); + } + } + + @Override + public void writeToEvent(CloudEvent event) { + if (traceparent != null) { + event.getExtensions().put(TRACEPARENT, this.traceparent); + } + if (tracestate != null) { + event.getExtensions().put(TRACESTATE, this.tracestate); + } + } + @Override public String toString() { return "DistributedTracingExtension{" + @@ -34,14 +58,14 @@ public String toString() { ", tracestate='" + tracestate + '\'' + '}'; } - + @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((traceparent == null) ? 0 + result = prime * result + ((traceparent == null) ? 0 : traceparent.hashCode()); - result = prime * result + ((tracestate == null) ? 0 + result = prime * result + ((tracestate == null) ? 0 : tracestate.hashCode()); return result; } @@ -67,70 +91,4 @@ public boolean equals(Object obj) { return false; return true; } - - /** - * The in-memory format for distributed tracing. - *
- * Details here - * @author fabiojose - * - */ - public static class Format implements ExtensionFormat { - - public static final String IN_MEMORY_KEY = "distributedTracing"; - public static final String TRACE_PARENT_KEY = "traceparent"; - public static final String TRACE_STATE_KEY = "tracestate"; - - private final InMemoryFormat memory; - private final Map transport = new HashMap<>(); - public Format(DistributedTracingExtension extension) { - Objects.requireNonNull(extension); - - memory = InMemoryFormat.of(IN_MEMORY_KEY, extension, - DistributedTracingExtension.class); - - transport.put(TRACE_PARENT_KEY, extension.getTraceparent()); - transport.put(TRACE_STATE_KEY, extension.getTracestate()); - } - - @Override - public InMemoryFormat memory() { - return memory; - } - - @Override - public Map transport() { - return transport; - } - } - - /** - * Unmarshals the {@link DistributedTracingExtension} based on map of extensions. - * @param exts - * @return - */ - public static Optional unmarshall( - Map exts) { - String traceparent = exts.get(Format.TRACE_PARENT_KEY); - String tracestate = exts.get(Format.TRACE_STATE_KEY); - - if(null!= traceparent && null!= tracestate) { - DistributedTracingExtension dte = new DistributedTracingExtension(); - dte.setTraceparent(traceparent); - dte.setTracestate(tracestate); - - InMemoryFormat inMemory = - InMemoryFormat.of(Format.IN_MEMORY_KEY, dte, - DistributedTracingExtension.class); - - return Optional.of( - ExtensionFormat.of(inMemory, - new SimpleEntry<>(Format.TRACE_PARENT_KEY, traceparent), - new SimpleEntry<>(Format.TRACE_STATE_KEY, tracestate)) - ); - - } - - return Optional.empty(); - } } diff --git a/api/src/main/java/io/cloudevents/extensions/ExtensionFormat.java b/api/src/main/java/io/cloudevents/extensions/ExtensionFormat.java deleted file mode 100644 index f2823ad6b..000000000 --- a/api/src/main/java/io/cloudevents/extensions/ExtensionFormat.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.extensions; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.stream.Collectors; - -/** - * Defines a way to add custom extension in the abstract envelop. - * - * @author fabiojose - * - */ -public interface ExtensionFormat { - - /** - * The in-memory format to be used by the structured content mode. - */ - InMemoryFormat memory(); - - /** - * The transport format to be used by the binary content mode. - */ - Map transport(); - - static ExtensionFormat of(final InMemoryFormat inMemory, - final String key, final String value) { - - final Map transport = new HashMap<>(); - transport.put(key, value); - - return new ExtensionFormat() { - @Override - public InMemoryFormat memory() { - return inMemory; - } - - @Override - public Map transport() { - return Collections.unmodifiableMap(transport); - } - }; - } - - @SafeVarargs - static ExtensionFormat of(final InMemoryFormat inMemory, - Entry ... transport){ - Objects.requireNonNull(inMemory); - Objects.requireNonNull(transport); - - final Map transports = Arrays.asList(transport) - .stream() - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - return new ExtensionFormat() { - @Override - public InMemoryFormat memory() { - return inMemory; - } - - @Override - public Map transport() { - return transports; - } - }; - } - - /** - * Marshals a collection of {@link ExtensionFormat} to {@code Map} - * @param extensions - * @return - */ - static Map marshal(Collection - extensions) { - - return extensions.stream() - .map(ExtensionFormat::transport) - .flatMap(t -> t.entrySet().stream()) - .collect(Collectors.toMap(Entry::getKey, - Entry::getValue)); - } -} diff --git a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java new file mode 100644 index 000000000..164f9e575 --- /dev/null +++ b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java @@ -0,0 +1,32 @@ +package io.cloudevents.extensions; + +import io.cloudevents.CloudEvent; +import io.cloudevents.Extension; + +import java.util.HashMap; +import java.util.function.Supplier; + +public class ExtensionsParser { + + private HashMap, Supplier> extensionFactories; + + public ExtensionsParser() { + this.extensionFactories = new HashMap<>(); + registerExtension(DistributedTracingExtension.class, DistributedTracingExtension::new); + } + + public void registerExtension(Class extensionClass, Supplier factory) { + this.extensionFactories.put(extensionClass, factory); + } + + public Extension parseExtension(Class extensionClass, CloudEvent event) { + Supplier factory = extensionFactories.get(extensionClass); + if (factory != null) { + Extension ext = factory.get(); + ext.readFromEvent(event); + return ext; + } + return null; + } + +} diff --git a/api/src/main/java/io/cloudevents/extensions/InMemoryFormat.java b/api/src/main/java/io/cloudevents/extensions/InMemoryFormat.java deleted file mode 100644 index f1bc8d306..000000000 --- a/api/src/main/java/io/cloudevents/extensions/InMemoryFormat.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.extensions; - -/** - * The in-memory format to be used by the structured content mode. - *
- * See details about in-memory format - * here - * @author fabiojose - * - */ -public interface InMemoryFormat { - - /** - * The in-memory format key - */ - String getKey(); - - /** - * The in-memory format value - */ - Object getValue(); - - /** - * The type reference for the value. That should be used during the - * unmarshal. - */ - Class getValueType(); - - public static InMemoryFormat of(final String key, final Object value, - final Class valueType) { - return new InMemoryFormat() { - @Override - public String getKey() { - return key; - } - - @Override - public Object getValue() { - return value; - } - - @Override - public Class getValueType() { - return valueType; - } - }; - } -} diff --git a/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java b/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java index 935e0d99c..5b581ac3d 100644 --- a/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java +++ b/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java @@ -39,13 +39,13 @@ import io.cloudevents.fun.FormatExtensionMapper; /** - * + * * @author fabiojose * */ public final class BinaryUnmarshaller { private BinaryUnmarshaller() {} - + /** * Gets a new builder instance * @param The attributes type @@ -53,11 +53,11 @@ private BinaryUnmarshaller() {} * @param

The payload type * @return */ - public static AttributeMapStep + public static AttributeMapStep builder() { return new Builder(); } - + public interface AttributeMapStep { /** * Maps the map of headers into map of attributes @@ -66,7 +66,7 @@ public interface AttributeMapStep { */ AttributeUmarshallStep map(BinaryFormatAttributeMapper unmarshaller); } - + public interface AttributeUmarshallStep { /** * Unmarshals the map of attributes into instance of {@link Attributes} @@ -75,7 +75,7 @@ public interface AttributeUmarshallStep { */ DataUnmarshallerStep map(AttributeUnmarshaller unmarshaller); } - + public interface DataUnmarshallerStep { /** * Unmarshals the payload into actual 'data' type @@ -83,10 +83,10 @@ public interface DataUnmarshallerStep { * @return */ DataUnmarshallerStep map(String mime, DataUnmarshaller unmarshaller); - + ExtensionsMapStep next(); } - + public interface ExtensionsMapStep { /** * Maps the headers map into map of extensions @@ -95,7 +95,7 @@ public interface ExtensionsMapStep { */ ExtensionsStep map(FormatExtensionMapper mapper); } - + public interface ExtensionsStepBegin { /** * Starts the configuration for extensions unmarshal @@ -103,28 +103,28 @@ public interface ExtensionsStepBegin { */ ExtensionsStep beginExtensions(); } - + public interface ExtensionsStep { /** * Unmarshals a extension, based on the map of extensions. - * + * *
*
* This is an optional step, because you do not have extensions or * do not want to process them at all. - * + * * @param unmarshaller * @return */ ExtensionsStep map(ExtensionUmarshaller unmarshaller); - + /** * Ends the configuration for extensions unmarshal * @return */ BuilderStep next(); } - + public interface BuilderStep
{ /** * Takes the builder to build {@link CloudEvent} instances @@ -133,9 +133,9 @@ public interface BuilderStep { */ HeadersStep builder(EventBuilder builder); } - + private static final class Builder implements - AttributeMapStep, + AttributeMapStep, AttributeUmarshallStep, DataUnmarshallerStep, ExtensionsMapStep, @@ -144,13 +144,13 @@ private static final class Builder implements HeadersStep, PayloadStep, UnmarshalStep{ - + private BinaryFormatAttributeMapper attributeMapper; private AttributeUnmarshaller attributeUnmarshaller; - private Map> dataUnmarshallers = + private Map> dataUnmarshallers = new HashMap<>(); private FormatExtensionMapper extensionMapper; - private Set extensionUnmarshallers = + private Set extensionUnmarshallers = new HashSet<>(); private EventBuilder eventBuilder; private Supplier> headersSupplier; @@ -173,7 +173,7 @@ public DataUnmarshallerStep map(String mime, DataUnmarshaller this.dataUnmarshallers.put(mime, unmarshaller); return this; } - + public Builder next() { return this; } @@ -211,24 +211,24 @@ public UnmarshalStep withPayload(Supplier

payload) { @Override public CloudEvent unmarshal() { - + Map headers = headersSupplier.get(); P payload = payloadSupplier.get(); - + Map attributesMap = attributeMapper.map(headers); - + A attributes = attributeUnmarshaller.unmarshal(attributesMap); - - T data = attributes.getMediaType() + + T data = attributes.getDataContentType() .map((mime) -> dataUnmarshallers.get(mime)) .filter(Objects::nonNull) - .map(unmarshaller -> + .map(unmarshaller -> unmarshaller.unmarshal(payload, attributes)) .orElse(null); - final Map extensionsMap = + final Map extensionsMap = extensionMapper.map(headers); - + List extensions = extensionUnmarshallers.stream() .map(unmarshaller -> @@ -236,8 +236,8 @@ public CloudEvent unmarshal() { .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); - + return eventBuilder.build(data, attributes, extensions); } - } + } } diff --git a/api/src/main/java/io/cloudevents/fun/EventBuilder.java b/api/src/main/java/io/cloudevents/fun/EventBuilder.java deleted file mode 100644 index d455133f9..000000000 --- a/api/src/main/java/io/cloudevents/fun/EventBuilder.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Collection; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; - -/** - * To build the event. - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface EventBuilder { - - /** - * Builds a new event using 'data', 'attributes' and 'extensions' - */ - CloudEvent build(T data, A attributes, - Collection extensions); - -} diff --git a/api/src/main/java/io/cloudevents/json/Json.java b/api/src/main/java/io/cloudevents/json/Json.java index a934db9d0..c7347c9f2 100644 --- a/api/src/main/java/io/cloudevents/json/Json.java +++ b/api/src/main/java/io/cloudevents/json/Json.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Map; import com.fasterxml.jackson.core.type.TypeReference; @@ -27,6 +28,7 @@ import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import io.cloudevents.Attributes; +import io.cloudevents.CloudEvent; import io.cloudevents.fun.DataMarshaller; import io.cloudevents.fun.DataUnmarshaller; @@ -38,12 +40,32 @@ public final class Json { // add Jackson datatype for ZonedDateTime MAPPER.registerModule(new Jdk8Module()); - final SimpleModule module = new SimpleModule(); + final SimpleModule module = new SimpleModule("Custom ZonedDateTime"); module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer()); MAPPER.registerModule(module); } + protected static final DateTimeFormatter RFC3339_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); + + public static String encode(final CloudEvent event) throws IllegalStateException { + + } + + public static byte[] encodeToBinary(final CloudEvent event) throws IllegalStateException { + + } + + public static CloudEvent decode(final byte[] binary) { + + } + + public static CloudEvent decode(final String string) { + + } + + // TODO remove all the stuff below + /** * Encode a POJO to JSON using the underlying Jackson mapper. * @@ -58,7 +80,7 @@ public static String encode(final Object obj) throws IllegalStateException { throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); } } - + /** * Encode a POJO to JSON using the underlying Jackson mapper. * @@ -73,27 +95,27 @@ public static byte[] binaryEncode(final Object obj) throws IllegalStateException throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); } } - + public static T fromInputStream(final InputStream inputStream, Class clazz) { try { return MAPPER.readValue(inputStream, clazz); } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " + throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); } } - + public static T fromInputStream(final InputStream inputStream, final TypeReference type) { try { return MAPPER.readValue(inputStream, type); } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " + throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); } } - + /** * Decode a given JSON string to a POJO of the given class type. * @@ -104,7 +126,7 @@ public static T fromInputStream(final InputStream inputStream, * @throws IllegalStateException when there is a parsing or invalid mapping. */ protected static T decodeValue(final String str, final Class clazz) throws IllegalStateException { - + if(null!= str && !"".equals(str.trim())) { try { return MAPPER.readValue(str.trim(), clazz); @@ -112,10 +134,10 @@ protected static T decodeValue(final String str, final Class clazz) throw throw new IllegalStateException("Failed to decode: " + e.getMessage()); } } - + return null; } - + protected static T binaryDecodeValue(byte[] payload, final Class clazz) { if(null!= payload) { try { @@ -146,13 +168,13 @@ public static T decodeValue(final String str, final TypeReference type) t } return null; } - + /** * Example of use: *

      * String someJson = "...";
      * Class clazz = Much.class;
-     * 
+     *
      * Json.decodeValue(someJson, CloudEventImpl.class, clazz);
      * 
* @param str The JSON String to parse @@ -167,11 +189,11 @@ public static T decodeValue(final String str, Class parametrized, Class...parameterClasses) { if(null!= str && !"".equals(str.trim())) { try { - JavaType type = + JavaType type = MAPPER.getTypeFactory() .constructParametricType(parametrized, parameterClasses); - + return MAPPER.readValue(str.trim(), type); } catch (Exception e) { throw new IllegalStateException("Failed to decode: " + e.getMessage(), e); @@ -179,13 +201,13 @@ public static T decodeValue(final String str, Class parametrized, } return null; } - + /** * Example of use: *
      * String someJson = "...";
      * Class clazz = Much.class;
-     * 
+     *
      * Json.decodeValue(someJson, CloudEventImpl.class, clazz);
      * 
* @param json The JSON byte array to parse @@ -200,11 +222,11 @@ public static T binaryDecodeValue(final byte[] json, Class parametrized, Class...parameterClasses) { if(null!= json) { try { - JavaType type = + JavaType type = MAPPER.getTypeFactory() .constructParametricType(parametrized, parameterClasses); - + return MAPPER.readValue(json, type); } catch (Exception e) { throw new IllegalStateException("Failed to decode: " + e.getMessage(), e); @@ -212,7 +234,7 @@ public static T binaryDecodeValue(final byte[] json, Class parametrized, } return null; } - + /** * Creates a JSON Data Unmarshaller * @param The 'data' type @@ -220,22 +242,22 @@ public static T binaryDecodeValue(final byte[] json, Class parametrized, * @param type The type of 'data' * @return A new instance of {@link DataUnmarshaller} */ - public static DataUnmarshaller + public static DataUnmarshaller umarshaller(Class type) { return (payload, attributes) -> Json.decodeValue(payload, type); } - + /** * Unmarshals a byte array into T type * @param The 'data' type * @param
The attributes type * @return The data objects */ - public static DataUnmarshaller + public static DataUnmarshaller binaryUmarshaller(Class type) { return (payload, attributes) -> Json.binaryDecodeValue(payload, type); } - + /** * Creates a JSON Data Marshaller that produces a {@link String} * @param The 'data' type @@ -245,7 +267,7 @@ public static T binaryDecodeValue(final byte[] json, Class parametrized, public static DataMarshaller marshaller() { return (data, headers) -> Json.encode(data); } - + /** * Marshalls the 'data' value as JSON, producing a byte array * @param The 'data' type diff --git a/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java b/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java index aec2fe55c..26aa4ef2c 100644 --- a/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java +++ b/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java @@ -27,11 +27,11 @@ public class ZonedDateTimeDeserializer extends StdDeserializer { private static final long serialVersionUID = 1L; - public ZonedDateTimeDeserializer() { + protected ZonedDateTimeDeserializer() { this(null); } - public ZonedDateTimeDeserializer(Class vc) { + protected ZonedDateTimeDeserializer(Class vc) { super(vc); } @@ -39,7 +39,7 @@ public ZonedDateTimeDeserializer(Class vc) { public ZonedDateTime deserialize(JsonParser jsonparser, DeserializationContext ctxt) throws IOException { // not serializing timezone data yet try { - return ZonedDateTime.parse(jsonparser.getText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME); + return ZonedDateTime.parse(jsonparser.getText(), Json.RFC3339_DATE_FORMAT); } catch (DateTimeException e) { throw new IllegalArgumentException("could not parse"); } diff --git a/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java b/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java index 484e0473d..21d154627 100644 --- a/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java +++ b/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java @@ -27,7 +27,7 @@ public class ZonedDateTimeSerializer extends StdSerializer { private static final long serialVersionUID = 6245182835980474796L; - public ZonedDateTimeSerializer() { + protected ZonedDateTimeSerializer() { this(null, false); } @@ -39,7 +39,7 @@ protected ZonedDateTimeSerializer(Class t, boolean dummy) { public void serialize(ZonedDateTime time, JsonGenerator generator, SerializerProvider provider) throws IOException { - generator.writeString(time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + generator.writeString(time.format(Json.RFC3339_DATE_FORMAT)); } diff --git a/api/src/main/java/io/cloudevents/v02/Accessor.java b/api/src/main/java/io/cloudevents/v02/Accessor.java deleted file mode 100644 index 8da66c578..000000000 --- a/api/src/main/java/io/cloudevents/v02/Accessor.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import java.util.Collection; -import java.util.Objects; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.ExtensionFormatAccessor; - -/** - * - * @author fabiojose - * - */ -public final class Accessor { - private Accessor() {} - - /** - * To get access the set of {@link ExtensionFormat} inside the - * event. - * - *
- *
- * This method follow the signature of - * {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)} - * - * @param cloudEvent - * @throws IllegalArgumentException When argument is not an instance of {@link CloudEventImpl} - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public static
Collection - extensionsOf(CloudEvent cloudEvent) { - Objects.requireNonNull(cloudEvent); - - if(cloudEvent instanceof CloudEventImpl) { - CloudEventImpl impl = (CloudEventImpl)cloudEvent; - return impl.getExtensionsFormats(); - } - - throw new IllegalArgumentException("Invalid instance type: " - + cloudEvent.getClass()); - } -} diff --git a/api/src/main/java/io/cloudevents/v02/AttributesImpl.java b/api/src/main/java/io/cloudevents/v02/AttributesImpl.java deleted file mode 100644 index d4ecff74c..000000000 --- a/api/src/main/java/io/cloudevents/v02/AttributesImpl.java +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import io.cloudevents.Attributes; -import io.cloudevents.json.ZonedDateTimeDeserializer; - -/** - * - * @author fabiojose - * @version 0.2 - */ -@JsonInclude(value = Include.NON_ABSENT) -public class AttributesImpl implements Attributes { - - @NotBlank - private final String type; - - @NotBlank - @Pattern(regexp = "0\\.2") - private final String specversion; - - @NotNull - private final URI source; - - @NotBlank - private final String id; - - private final ZonedDateTime time; - private final URI schemaurl; - private final String contenttype; - - AttributesImpl(String type, String specversion, URI source, - String id, ZonedDateTime time, URI schemaurl, String contenttype) { - this.type = type; - this.specversion = specversion; - this.source = source; - this.id = id; - this.time = time; - this.schemaurl = schemaurl; - this.contenttype = contenttype; - } - - /** - * Type of occurrence which has happened. Often this property is used for - * routing, observability, policy enforcement, etc. - */ - public String getType() { - return type; - } - - /** - * ID of the event. The semantics of this string are explicitly - * undefined to ease the implementation of producers. Enables - * deduplication. - */ - public String getId() { - return id; - } - - /** - * The version of the CloudEvents specification which the event uses. - * This enables the interpretation of the context. - */ - public String getSpecversion() { - return specversion; - } - - /** - * This describes the event producer. Often this will include - * information such as the type of the event source, the organization - * publishing the event, and some unique identifiers. - * The exact syntax and semantics behind the data encoded in the URI - * is event producer defined. - */ - public URI getSource() { - return source; - } - - /** - * Timestamp of when the event happened. - */ - @JsonDeserialize(using = ZonedDateTimeDeserializer.class) - public Optional getTime() { - return Optional.ofNullable(time); - } - - /** - * A link to the schema that the data attribute adheres to. - */ - public Optional getSchemaurl() { - return Optional.ofNullable(schemaurl); - } - - /** - * Describe the data encoding format - */ - public Optional getContenttype() { - return Optional.ofNullable(contenttype); - } - - /** - * {@inheritDoc} - */ - public Optional getMediaType() { - return getContenttype(); - } - - @JsonCreator - public static AttributesImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("specversion") String specversion, - @JsonProperty("type") String type, - @JsonProperty("time") ZonedDateTime time, - @JsonProperty("schemaurl") URI schemaurl, - @JsonProperty("contenttype") String contenttype) { - - return new AttributesImpl(type, specversion, source, id, time, - schemaurl, contenttype); - } - - /** - * The attribute unmarshaller for the binary format, that receives a - * {@code Map} with attributes names as String and values as String. - */ - public static AttributesImpl unmarshal(Map attributes) { - String type = attributes.get(ContextAttributes.type.name()); - ZonedDateTime time = - Optional.ofNullable(attributes.get(ContextAttributes.time.name())) - .map((t) -> ZonedDateTime.parse(t, - ISO_ZONED_DATE_TIME)) - .orElse(null); - - String specversion = attributes.get(ContextAttributes.specversion.name()); - URI source = URI.create(attributes.get(ContextAttributes.source.name())); - - URI schemaurl = - Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name())) - .map(URI::create) - .orElse(null); - - String id = attributes.get(ContextAttributes.id.name()); - - String contenttype = - attributes.get(ContextAttributes.contenttype.name()); - - - return AttributesImpl.build(id, source, specversion, type, - time, schemaurl, contenttype); - } - - /** - * Creates the marshaller instance to marshall {@link AttributesImpl} as - * a {@link Map} of strings - */ - public static Map marshal(AttributesImpl attributes) { - Objects.requireNonNull(attributes); - - Map result = new HashMap<>(); - - result.put(ContextAttributes.type.name(), - attributes.getType()); - result.put(ContextAttributes.specversion.name(), - attributes.getSpecversion()); - result.put(ContextAttributes.source.name(), - attributes.getSource().toString()); - result.put(ContextAttributes.id.name(), - attributes.getId()); - - attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.time.name(), - value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); - attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.schemaurl.name(), - schema.toString())); - attributes.getContenttype().ifPresent((ct) -> result.put(ContextAttributes.contenttype.name(), ct)); - - return result; - } -} diff --git a/api/src/main/java/io/cloudevents/v02/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v02/CloudEventBuilder.java deleted file mode 100644 index f6f651c99..000000000 --- a/api/src/main/java/io/cloudevents/v02/CloudEventBuilder.java +++ /dev/null @@ -1,254 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - *

- * Licensed 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 io.cloudevents.v02; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; - -import io.cloudevents.Builder; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.EventBuilder; - -import static java.lang.String.format; - -/** - * CloudEvent instances builder - * - * @author fabiojose - * @version 0.2 - */ -public class CloudEventBuilder implements EventBuilder, - Builder { - private CloudEventBuilder() { - } - - private static Validator VALIDATOR; - - private static final String SPEC_VERSION = "0.2"; - private static final String MESSAGE_SEPARATOR = ", "; - private static final String MESSAGE = "'%s' %s"; - private static final String ERR_MESSAGE = "invalid payload: %s"; - - private String type; - private String id; - private URI source; - - private ZonedDateTime time; - private URI schemaurl; - private String contenttype; - private T data; - - private final Set extensions = new HashSet<>(); - private Validator validator; - - private static Validator getValidator() { - if (null == VALIDATOR) { - VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); - } - return VALIDATOR; - } - - /** - * Gets a brand new builder instance - * @param The 'data' type - */ - public static CloudEventBuilder builder() { - return new CloudEventBuilder<>(); - } - - /** - * - * @param The 'data' type - * @param base A base event to copy {@link CloudEvent#getAttributes()}, - * {@link CloudEvent#getData()} and {@link CloudEvent#getExtensions()} - * @return - */ - public static CloudEventBuilder builder( - CloudEvent base) { - - Objects.requireNonNull(base); - - CloudEventBuilder result = new CloudEventBuilder<>(); - - AttributesImpl attributes = base.getAttributes(); - - result - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(result::withTime); - attributes.getSchemaurl().ifPresent(result::withSchemaurl); - attributes.getContenttype().ifPresent(result::withContenttype); - Accessor.extensionsOf(base).forEach(result::withExtension); - base.getData().ifPresent(result::withData); - - return result; - } - - /** - * - * @param the type of 'data' - * @param data the value of data - * @param attributes the context attributes - * @return An new {@link CloudEventImpl} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - public static CloudEventImpl of(T data, AttributesImpl attributes, - Collection extensions, Validator validator) { - CloudEventBuilder builder = new CloudEventBuilder() - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(builder::withTime); - attributes.getSchemaurl().ifPresent(builder::withSchemaurl); - attributes.getContenttype().ifPresent(builder::withContenttype); - extensions.forEach(builder::withExtension); - - return builder - .withData(data) - .withValidator(validator) - .build(); - } - - @Override - public CloudEvent build(T data, AttributesImpl attributes, - Collection extensions) { - return CloudEventBuilder.of(data, attributes, extensions, this.validator); - } - - /** - * {@inheritDoc} - * @return An new {@link CloudEventImpl} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - @Override - public CloudEventImpl build() { - AttributesImpl attributes = new AttributesImpl(type, SPEC_VERSION, - source, id, time, schemaurl, contenttype); - - CloudEventImpl event = new CloudEventImpl<>(attributes, data, extensions); - - if (validator == null) { - validator = getValidator(); - } - Set> violations = - validator.validate(event); - - violations.addAll(validator.validate(event.getAttributes())); - - final String errs = - violations.stream() - .map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage())) - .collect(Collectors.joining(MESSAGE_SEPARATOR)); - - Optional.ofNullable( - "".equals(errs) ? null : errs - - ).ifPresent((e) -> { - throw new IllegalStateException(format(ERR_MESSAGE, e)); - }); - - return event; - } - - public CloudEventBuilder withType(String type) { - this.type = type; - return this; - } - - public CloudEventBuilder withId(String id) { - this.id = id; - return this; - } - - public CloudEventBuilder withSource(URI source) { - this.source = source; - return this; - } - - public CloudEventBuilder withTime(ZonedDateTime time) { - this.time = time; - return this; - } - - public CloudEventBuilder withSchemaurl(URI schemaurl) { - this.schemaurl = schemaurl; - return this; - } - - public CloudEventBuilder withContenttype(String contenttype) { - this.contenttype = contenttype; - return this; - } - - public CloudEventBuilder withData(T data) { - this.data = data; - return this; - } - - public CloudEventBuilder withExtension(ExtensionFormat extension) { - this.extensions.add(extension); - return this; - } - - public CloudEventBuilder withValidator(Validator validator) { - this.validator = validator; - return this; - } - - public CloudEvent - build(CloudEvent base, String id, TT newData) { - return build(base, id, newData, null); - } - - public CloudEvent - build(CloudEvent base, String id, TT newData, Validator validator) { - Objects.requireNonNull(base); - - AttributesImpl attributes = base.getAttributes(); - - CloudEventBuilder builder = new CloudEventBuilder() - .withId(id) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(builder::withTime); - attributes.getSchemaurl().ifPresent(builder::withSchemaurl); - attributes.getContenttype().ifPresent(builder::withContenttype); - Collection extensions = Accessor.extensionsOf(base); - extensions.forEach(builder::withExtension); - - return builder - .withData(newData) - .withValidator(validator) - .build(); - } -} diff --git a/api/src/main/java/io/cloudevents/v02/CloudEventImpl.java b/api/src/main/java/io/cloudevents/v02/CloudEventImpl.java deleted file mode 100644 index 00eae201f..000000000 --- a/api/src/main/java/io/cloudevents/v02/CloudEventImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.constraints.NotNull; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; - -/** - * The event implementation - * - * @author fabiojose - * - */ -@JsonInclude(value = Include.NON_ABSENT) -public class CloudEventImpl implements CloudEvent { - - @JsonIgnore - @NotNull - private final AttributesImpl attributes; - - private final T data; - - @NotNull - private final Map extensions; - - private final Set extensionsFormats; - - CloudEventImpl(AttributesImpl attributes, T data, - Set extensions) { - - this.attributes = attributes; - - this.data = data; - - this.extensions = extensions.stream() - .map(ExtensionFormat::memory) - .collect(Collectors.toMap(InMemoryFormat::getKey, - InMemoryFormat::getValue)); - - this.extensionsFormats = extensions; - } - - /** - * Used by the {@link Accessor} to access the set of {@link ExtensionFormat} - */ - Set getExtensionsFormats() { - return extensionsFormats; - } - - @Override - @JsonUnwrapped - public AttributesImpl getAttributes() { - return this.attributes; - } - - /** - * The event payload. The payload depends on the eventType, - * schemaURL and eventTypeVersion, the payload is encoded into - * a media format which is specified by the contentType attribute - * (e.g. application/json). - */ - @Override - public Optional getData() { - return Optional.ofNullable(data); - } - - @Override - public byte[] getDataBase64() { - return null; - } - - @Override - @JsonAnyGetter - public Map getExtensions() { - return Collections.unmodifiableMap(extensions); - } - - /** - * The unique method that allows mutation. Used by - * Jackson Framework to inject the extensions. - * - * @param name Extension name - * @param value Extension value - */ - @JsonAnySetter - void addExtension(String name, Object value) { - extensions.put(name, value); - } - - @JsonCreator - public static CloudEventImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("type") String type, - @JsonProperty("time") ZonedDateTime time, - @JsonProperty("schemaurl") URI schemaurl, - @JsonProperty("contenttype") String contenttype, - @JsonProperty("data") T data) { - - return CloudEventBuilder.builder() - .withId(id) - .withSource(source) - .withType(type) - .withTime(time) - .withSchemaurl(schemaurl) - .withContenttype(contenttype) - .withData(data) - .build(); - } -} diff --git a/api/src/main/java/io/cloudevents/v02/ContextAttributes.java b/api/src/main/java/io/cloudevents/v02/ContextAttributes.java deleted file mode 100644 index 4517fc4f9..000000000 --- a/api/src/main/java/io/cloudevents/v02/ContextAttributes.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * The specification reserved words: the context attributes - * - * @author fabiojose - * - */ -public enum ContextAttributes { - - id, - source, - specversion, - type, - time, - schemaurl, - contenttype; - - public static final List VALUES = - Arrays.stream(ContextAttributes.values()) - .map(Enum::name) - .collect(Collectors.toList()); -} diff --git a/api/src/main/java/io/cloudevents/v02/http/AttributeMapper.java b/api/src/main/java/io/cloudevents/v02/http/AttributeMapper.java deleted file mode 100644 index 77ecfc6e2..000000000 --- a/api/src/main/java/io/cloudevents/v02/http/AttributeMapper.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static java.util.stream.Collectors.toMap; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; - -import io.cloudevents.fun.BinaryFormatAttributeMapper; -import io.cloudevents.v02.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public class AttributeMapper { - private AttributeMapper() {} - - static final String HEADER_PREFIX = "ce-"; - - /** - * Following the signature of {@link BinaryFormatAttributeMapper#map(Map)} - * @param headers Map of HTTP request - * @return Map with spec attributes and values without parsing - * @see ContextAttributes - */ - public static Map map(final Map headers) { - Objects.requireNonNull(headers); - - final AtomicReference>> ct = - new AtomicReference<>(); - - ct.set(Optional.empty()); - - Map result = headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .peek(header -> { - if("content-type".equals(header.getKey())) { - ct.set(Optional.ofNullable(header)); - } - }) - .filter(header -> header.getKey().startsWith(HEADER_PREFIX)) - .map(header -> new SimpleEntry<>(header.getKey() - .substring(HEADER_PREFIX.length()), header.getValue())) - - .map(header -> new SimpleEntry<>(header.getKey(), - header.getValue().toString())) - - .collect(toMap(Entry::getKey, Entry::getValue)); - - ct.get().ifPresent(contentType -> { - result.put(ContextAttributes.contenttype.name(), - contentType.getValue().toString()); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v02/http/ExtensionMapper.java b/api/src/main/java/io/cloudevents/v02/http/ExtensionMapper.java deleted file mode 100644 index 1e703bba6..000000000 --- a/api/src/main/java/io/cloudevents/v02/http/ExtensionMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import java.util.AbstractMap.SimpleEntry; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatExtensionMapper; -import io.cloudevents.v02.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public class ExtensionMapper { - private ExtensionMapper() {} - - private static final List RESERVED_HEADERS = - ContextAttributes.VALUES.stream() - .map(attribute -> AttributeMapper - .HEADER_PREFIX + attribute) - .collect(Collectors.toList()); - static { - RESERVED_HEADERS.add("content-type"); - }; - - /** - * Following the signature of {@link FormatExtensionMapper} - * @param headers The HTTP headers - * @return The potential extensions without parsing - */ - public static Map map(Map headers) { - Objects.requireNonNull(headers); - - // remove all reserved words and the remaining may be extensions - return - headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue().toString())) - .filter(header -> !RESERVED_HEADERS.contains(header.getKey())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } - -} diff --git a/api/src/main/java/io/cloudevents/v02/http/HeaderMapper.java b/api/src/main/java/io/cloudevents/v02/http/HeaderMapper.java deleted file mode 100644 index ce89f088f..000000000 --- a/api/src/main/java/io/cloudevents/v02/http/HeaderMapper.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static io.cloudevents.v02.http.AttributeMapper.HEADER_PREFIX; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatHeaderMapper; -import io.cloudevents.v02.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public final class HeaderMapper { - private HeaderMapper() {} - - private static final String HTTP_CONTENT_TYPE = "Content-Type"; - - /** - * Following the signature of {@link FormatHeaderMapper} - * @param attributes The map of attributes created by {@link AttributeMapper} - * @param extensions The map of extensions created by {@link ExtensionMapper} - * @return The map of HTTP Headers - */ - public static Map map(Map attributes, - Map extensions) { - Objects.requireNonNull(attributes); - Objects.requireNonNull(extensions); - - Map result = attributes.entrySet() - .stream() - .filter(attribute -> null!= attribute.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .filter(header -> !header.getKey() - .equals(ContextAttributes.contenttype.name())) - .map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(), - header.getValue())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - result.putAll( - extensions.entrySet() - .stream() - .filter(extension -> null!= extension.getValue()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) - ); - - Optional.ofNullable(attributes.get(ContextAttributes.contenttype.name())) - .ifPresent((ct) -> { - result.put(HTTP_CONTENT_TYPE, ct); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v02/http/Marshallers.java b/api/src/main/java/io/cloudevents/v02/http/Marshallers.java deleted file mode 100644 index 32dd65756..000000000 --- a/api/src/main/java/io/cloudevents/v02/http/Marshallers.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.BinaryMarshaller; -import io.cloudevents.format.StructuredMarshaller; -import io.cloudevents.format.Wire; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.json.Json; -import io.cloudevents.v02.Accessor; -import io.cloudevents.v02.AttributesImpl; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public class Marshallers { - private Marshallers() {} - - private static final Map NO_HEADERS = - new HashMap(); - - /** - * Builds a Binary Content Mode marshaller to marshal cloud events as JSON for - * HTTP Transport Binding - * - * @param The 'data' type - * @return A step to provide the {@link CloudEventImpl} and marshal as JSON - * @see BinaryMarshaller - */ - public static EventStep binary() { - return - BinaryMarshaller. - builder() - .map(AttributesImpl::marshal) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map) - .map(Json.marshaller()::marshal) - .builder(Wire::new); - } - - /** - * Builds a Structured Content Mode marshaller to marshal cloud event as JSON for - * HTTP Transport Binding - * @param The 'data' type - * @return A step to provider the {@link CloudEventImpl} and marshal as JSON - * @see StructuredMarshaller - */ - public static EventStep structured() { - return - StructuredMarshaller. - builder() - .mime("Content-Type", "application/cloudevents+json") - .map((event) -> { - return Json., String> - marshaller().marshal(event, NO_HEADERS); - }) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map); - } -} diff --git a/api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java b/api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java deleted file mode 100644 index be92c5705..000000000 --- a/api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.cloudevents.v02.http; - -import javax.validation.Validator; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.format.BinaryUnmarshaller; -import io.cloudevents.format.StructuredUnmarshaller; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.json.Json; -import io.cloudevents.v02.AttributesImpl; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public class Unmarshallers { - private Unmarshallers() {} - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type) { - return binary(type, null); - } - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @param validator Provide an existing instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type, Validator validator) { - return - BinaryUnmarshaller.builder() - .map(AttributeMapper::map) - .map(AttributesImpl::unmarshal) - .map("application/json", Json.umarshaller(type)::unmarshal) - .next() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .builder(CloudEventBuilder.builder().withValidator(validator)::build); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData) { - return structured(typeOfData, null); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @param validator Provided instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData, Validator validator) { - - return - StructuredUnmarshaller. - builder() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .map((payload, extensions) -> { - CloudEventImpl event = - Json.decodeValue(payload, CloudEventImpl.class, typeOfData); - CloudEventBuilder builder = - CloudEventBuilder.builder(event); - extensions.get().forEach(builder::withExtension); - - return builder.withValidator(validator).build(); - }); - } -} diff --git a/api/src/main/java/io/cloudevents/v03/Accessor.java b/api/src/main/java/io/cloudevents/v03/Accessor.java deleted file mode 100644 index a8ea530ec..000000000 --- a/api/src/main/java/io/cloudevents/v03/Accessor.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.cloudevents.v03; - -import java.util.Collection; -import java.util.Objects; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.ExtensionFormatAccessor; - -/** - * - * @author fabiojose - * - */ -public class Accessor { - private Accessor() {} - - /** - * To get access the set of {@link ExtensionFormat} inside the - * event. - * - *
- *
- * This method follow the signature of - * {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)} - * - * @param cloudEvent - * @throws IllegalArgumentException When argument is not an instance - * of {@link CloudEventImpl} - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public static
Collection - extensionsOf(CloudEvent cloudEvent) { - Objects.requireNonNull(cloudEvent); - - if(cloudEvent instanceof CloudEventImpl) { - CloudEventImpl impl = (CloudEventImpl)cloudEvent; - return impl.getExtensionsFormats(); - } - - throw new IllegalArgumentException("Invalid instance type: " - + cloudEvent.getClass()); - } -} diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index f40f76825..4aa927346 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -18,6 +18,7 @@ import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; import java.net.URI; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -31,58 +32,42 @@ import javax.validation.constraints.Size; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.cloudevents.Attributes; -import io.cloudevents.json.ZonedDateTimeDeserializer; +import io.cloudevents.SpecVersion; /** * The event attributes implementation for v0.3 - * + * * @author fabiojose + * @author slinkydeveloper * */ -@JsonInclude(value = Include.NON_ABSENT) public class AttributesImpl implements Attributes { - - @NotBlank + private final String id; - - @NotNull + private final URI source; - - @NotBlank - @Pattern(regexp = "0\\.3") - private final String specversion; - - @NotBlank + private final String type; - - @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + private final ZonedDateTime time; private final URI schemaurl; - - @Pattern(regexp = "base64") - private final String datacontentencoding; + private final String datacontenttype; - - @Size(min = 1) + private final String subject; - - AttributesImpl(String id, URI source, String specversion, String type, - ZonedDateTime time, URI schemaurl, String datacontentencoding, + + AttributesImpl(String id, URI source, String type, + ZonedDateTime time, URI schemaurl, String datacontenttype, String subject) { this.id = id; this.source = source; - this.specversion = specversion; this.type = type; - + this.time = time; this.schemaurl = schemaurl; - this.datacontentencoding = datacontentencoding; this.datacontenttype = datacontenttype; this.subject = subject; } @@ -93,8 +78,8 @@ public String getId() { public URI getSource() { return source; } - public String getSpecversion() { - return specversion; + public SpecVersion getSpecVersion() { + return SpecVersion.V03; } public String getType() { return type; @@ -102,32 +87,29 @@ public String getType() { public Optional getTime() { return Optional.ofNullable(time); } - public Optional getSchemaurl() { + public Optional getDataSchema() { + return getSchemaUrl(); + } + public Optional getSchemaUrl() { return Optional.ofNullable(schemaurl); } - public Optional getDatacontentencoding() { - return Optional.ofNullable(datacontentencoding); - } - public Optional getDatacontenttype() { - return Optional.ofNullable(datacontenttype); - } /** * {@inheritDoc} */ - public Optional getMediaType() { - return getDatacontenttype(); + public Optional getDataContentType() { + return Optional.ofNullable(datacontenttype); } - public Optional getSubject() { + + public Optional getSubject() { return Optional.ofNullable(subject); } - + @Override public String toString() { - return "AttributesImpl [id=" + id + ", source=" + source - + ", specversion=" + specversion + ", type=" + type - + ", time=" + time + ", schemaurl=" + schemaurl - + ", datacontentencoding=" + datacontentencoding - + ", datacontenttype=" + datacontenttype + ", subject=" + return "AttributesImpl [id=" + id + ", source=" + source + + ", specversion=" + SpecVersion.V03 + ", type=" + type + + ", time=" + time + ", schemaurl=" + schemaurl + + ", datacontenttype=" + datacontenttype + ", subject=" + subject + "]"; } @@ -145,72 +127,72 @@ public static AttributesImpl build( @JsonProperty("datacontentenconding") String datacontentencoding, @JsonProperty("datacontenttype") String datacontenttype, @JsonProperty("subject") String subject) { - + return new AttributesImpl(id, source, specversion, type, time, schemaurl, datacontentencoding, datacontenttype, subject); } - + /** - * Creates the marshaller instance to marshall {@link AttributesImpl} as + * Creates the marshaller instance to marshall {@link AttributesImpl} as * a {@link Map} of strings */ public static Map marshal(AttributesImpl attributes) { Objects.requireNonNull(attributes); - + Map result = new HashMap<>(); - result.put(ContextAttributes.type.name(), + result.put(ContextAttributes.TYPE.name(), attributes.getType()); - result.put(ContextAttributes.specversion.name(), - attributes.getSpecversion()); - result.put(ContextAttributes.source.name(), + result.put(ContextAttributes.SPECVERSION.name(), + attributes.getSpecVersion()); + result.put(ContextAttributes.SOURCE.name(), attributes.getSource().toString()); - result.put(ContextAttributes.id.name(), + result.put(ContextAttributes.ID.name(), attributes.getId()); - - attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.time.name(), + + attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.TIME.name(), value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); - attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.schemaurl.name(), + attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.SCHEMAURL.name(), schema.toString())); - attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.datacontenttype.name(), ct)); - attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.datacontentencoding.name(), dce)); - attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.subject.name(), subject)); + attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.DATACONTENTTYPE.name(), ct)); + attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.DATACONTENTENCODING.name(), dce)); + attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); return result; } - + /** * The attribute unmarshaller for the binary format, that receives a * {@code Map} with attributes names as String and value as String. */ public static AttributesImpl unmarshal(Map attributes) { - String type = attributes.get(ContextAttributes.type.name()); + String type = attributes.get(ContextAttributes.TYPE.name()); ZonedDateTime time = - Optional.ofNullable(attributes.get(ContextAttributes.time.name())) + Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) .map((t) -> ZonedDateTime.parse(t, ISO_ZONED_DATE_TIME)) .orElse(null); - - String specversion = attributes.get(ContextAttributes.specversion.name()); - URI source = URI.create(attributes.get(ContextAttributes.source.name())); - - URI schemaurl = - Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name())) + + String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); + URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); + + URI schemaurl = + Optional.ofNullable(attributes.get(ContextAttributes.SCHEMAURL.name())) .map(URI::create) .orElse(null); - - String id = attributes.get(ContextAttributes.id.name()); - - String datacontenttype = - attributes.get(ContextAttributes.datacontenttype.name()); - - String datacontentencoding = - attributes.get(ContextAttributes.datacontentencoding.name()); - - String subject = attributes.get(ContextAttributes.subject.name()); - + + String id = attributes.get(ContextAttributes.ID.name()); + + String datacontenttype = + attributes.get(ContextAttributes.DATACONTENTTYPE.name()); + + String datacontentencoding = + attributes.get(ContextAttributes.DATACONTENTENCODING.name()); + + String subject = attributes.get(ContextAttributes.SUBJECT.name()); + return AttributesImpl.build(id, source, specversion, type, time, schemaurl, datacontentencoding, datacontenttype, subject); } -} \ No newline at end of file +} diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java index 0431b6fd1..8567c4001 100644 --- a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java @@ -15,24 +15,11 @@ */ package io.cloudevents.v03; -import static java.lang.String.format; - import java.net.URI; import java.time.ZonedDateTime; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; +import io.cloudevents.common.BaseCloudEventBuilder; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.EventBuilder; /** * The event builder. @@ -40,17 +27,11 @@ * @author fabiojose * */ -public final class CloudEventBuilder implements - EventBuilder { - - private CloudEventBuilder() {} - - private static Validator VALIDATOR; +public final class CloudEventBuilder extends BaseCloudEventBuilder { - public static final String SPEC_VERSION = "0.3"; - private static final String MESSAGE_SEPARATOR = ", "; - private static final String MESSAGE = "'%s' %s"; - private static final String ERR_MESSAGE = "invalid payload: %s"; + public CloudEventBuilder() { + super(); + } private String id; private URI source; @@ -59,199 +40,53 @@ private CloudEventBuilder() {} private ZonedDateTime time; private URI schemaurl; - private String datacontentencoding; private String datacontenttype; private String subject; - private T data; - - private final Set extensions = new HashSet<>(); - private Validator validator; - - private static Validator getValidator() { - if(null== VALIDATOR) { - VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); - } - return VALIDATOR; - } - - /** - * Gets a brand new builder instance - * @param The 'data' type - */ - public static CloudEventBuilder builder() { - return new CloudEventBuilder<>(); - } - - public static CloudEventBuilder builder( - CloudEvent base) { - Objects.requireNonNull(base); - - CloudEventBuilder result = new CloudEventBuilder<>(); - - AttributesImpl attributes = base.getAttributes(); - - result - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(result::withTime); - attributes.getSchemaurl().ifPresent(result::withSchemaurl); - attributes.getDatacontenttype().ifPresent(result::withDatacontenttype); - attributes.getDatacontentencoding().ifPresent(result::withDatacontentencoding); - attributes.getSubject().ifPresent(result::withSubject); - Accessor.extensionsOf(base).forEach(result::withExtension); - base.getData().ifPresent(result::withData); - - return result; - } - - /** - * Build an event from data and attributes - * @param the type of 'data' - * @param data the value of data - * @param attributes the context attributes - * @param extensions the extension attributes - * @return An new {@link CloudEventImpl} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - public static CloudEventImpl of(T data, AttributesImpl attributes, - Collection extensions) { - return of(data, attributes, extensions, null); - } - /** - * Build an event from data and attributes - * @param the type of 'data' - * @param data the value of data - * @param attributes the context attributes - * @param extensions the extension attributes - * @param validator existing instance of a validator - * @return An new {@link CloudEventImpl} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - public static CloudEventImpl of(T data, AttributesImpl attributes, - Collection extensions, Validator validator) { - CloudEventBuilder builder = CloudEventBuilder.builder() - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(builder::withTime); - attributes.getSchemaurl().ifPresent(builder::withSchemaurl); - attributes.getDatacontentencoding().ifPresent(builder::withDatacontentencoding); - attributes.getDatacontenttype().ifPresent(builder::withDatacontenttype); - attributes.getSubject().ifPresent(builder::withSubject); - extensions.forEach(builder::withExtension); - - return builder - .withData(data) - .withValidator(validator) - .build(); - } - - @Override - public CloudEvent build(T data, AttributesImpl attributes, - Collection extensions){ - return CloudEventBuilder.of(data, attributes, extensions, this.validator); - } - - /** - * - * @return An new {@link CloudEvent} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - public CloudEventImpl build() { - - AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION, - type, time, schemaurl, datacontentencoding, datacontenttype, - subject); - - CloudEventImpl cloudEvent = - new CloudEventImpl<>(attributes, data, extensions); - - if(validator == null) { - validator = getValidator(); - } - - Set> violations = - validator.validate(cloudEvent); - - violations.addAll(validator.validate(cloudEvent.getAttributes())); - - final String errs = - violations.stream() - .map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage())) - .collect(Collectors.joining(MESSAGE_SEPARATOR)); - - Optional.ofNullable( - "".equals(errs) ? null : errs - - ).ifPresent((e) -> { - throw new IllegalStateException(format(ERR_MESSAGE, e)); - }); - - return cloudEvent; - } - - public CloudEventBuilder withId(String id) { + public CloudEventBuilder withId(String id) { this.id = id; return this; } - public CloudEventBuilder withSource(URI source) { + public CloudEventBuilder withSource(URI source) { this.source = source; return this; } - public CloudEventBuilder withType(String type) { + public CloudEventBuilder withType(String type) { this.type = type; return this; } - public CloudEventBuilder withTime(ZonedDateTime time) { + public CloudEventBuilder withTime(ZonedDateTime time) { this.time = time; return this; } - public CloudEventBuilder withSchemaurl(URI schemaurl) { - this.schemaurl = schemaurl; - return this; - } - - public CloudEventBuilder withDatacontentencoding( - String datacontentencoding) { - this.datacontentencoding = datacontentencoding; - return this; - } - - public CloudEventBuilder withDatacontenttype( - String datacontenttype) { - this.datacontenttype = datacontenttype; + public CloudEventBuilder withSubject(String subject) { + this.subject = subject; return this; } - public CloudEventBuilder withSubject( - String subject) { - this.subject = subject; + @Override + public CloudEventBuilder withDataContentType(String contentType) { + this.datacontenttype = contentType; return this; } - public CloudEventBuilder withData(T data) { - this.data = data; + public CloudEventBuilder withSchemaUrl(URI schemaUrl) { + this.schemaurl = schemaUrl; return this; } - public CloudEventBuilder withExtension(ExtensionFormat extension) { - this.extensions.add(extension); + @Override + protected CloudEventBuilder withDataSchema(URI dataSchema) { + this.schemaurl = dataSchema; return this; } - public CloudEventBuilder withValidator(Validator validator) { - this.validator = validator; - return this; + @Override + protected AttributesImpl buildAttributes() { + return new AttributesImpl(id, source, type, time, schemaurl, datacontenttype, subject); } } diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventImpl.java b/api/src/main/java/io/cloudevents/v03/CloudEventImpl.java deleted file mode 100644 index a83f6b8b8..000000000 --- a/api/src/main/java/io/cloudevents/v03/CloudEventImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.constraints.NotNull; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.annotation.JsonInclude.Include; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; - -/** - * The event implementation - * - * @author fabiojose - * - */ -@JsonInclude(value = Include.NON_ABSENT) -public class CloudEventImpl implements CloudEvent { - - @JsonIgnore - @NotNull - private final AttributesImpl attributes; - - private final T data; - - @NotNull - private final Map extensions; - - private final Set extensionsFormats; - - CloudEventImpl(AttributesImpl attributes, T data, - Set extensions) { - this.attributes = attributes; - this.data = data; - - this.extensions = extensions.stream() - .map(ExtensionFormat::memory) - .collect(Collectors.toMap(InMemoryFormat::getKey, - InMemoryFormat::getValue)); - - this.extensionsFormats = extensions; - } - - /** - * Used by the {@link Accessor} to access the set of {@link ExtensionFormat} - */ - Set getExtensionsFormats() { - return extensionsFormats; - } - - @Override - @JsonUnwrapped - public AttributesImpl getAttributes() { - return attributes; - } - - @Override - public Optional getData() { - return Optional.ofNullable(data); - } - - @Override - public byte[] getDataBase64() { - return null; - } - - @Override - @JsonAnyGetter - public Map getExtensions() { - return Collections.unmodifiableMap(extensions); - } - - /** - * The unique method that allows mutation. Used by - * Jackson Framework to inject the extensions. - * - * @param name Extension name - * @param value Extension value - */ - @JsonAnySetter - void addExtension(String name, Object value) { - extensions.put(name, value); - } - - /** - * Used by the Jackson Framework to unmarshall. - */ - @JsonCreator - public static CloudEventImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("type") String type, - @JsonProperty("time") ZonedDateTime time, - @JsonProperty("schemaurl") URI schemaurl, - @JsonProperty("datacontentencoding") String datacontentencoding, - @JsonProperty("datacontenttype") String datacontenttype, - @JsonProperty("subject") String subject, - @JsonProperty("data") T data) { - - return CloudEventBuilder.builder() - .withId(id) - .withSource(source) - .withType(type) - .withTime(time) - .withSchemaurl(schemaurl) - .withDatacontentencoding(datacontentencoding) - .withDatacontenttype(datacontenttype) - .withData(data) - .withSubject(subject) - .build(); - } - -} diff --git a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java index 0fc384487..1b6f686c6 100644 --- a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java @@ -21,24 +21,25 @@ /** * The specification reserved words: the context attributes - * + * * @author fabiojose * */ public enum ContextAttributes { - id, - source, - specversion, - type, - time, - schemaurl, - datacontenttype, - datacontentencoding, - subject; - - public static final List VALUES = + ID, + SOURCE, + SPECVERSION, + TYPE, + TIME, + SCHEMAURL, + DATACONTENTTYPE, + DATACONTENTENCODING, + SUBJECT; + + public static final List VALUES = Arrays.stream(ContextAttributes.values()) .map(Enum::name) + .map(String::toLowerCase) .collect(Collectors.toList()); } diff --git a/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java b/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java index fcff367ab..9e00b2063 100644 --- a/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java +++ b/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java @@ -29,7 +29,7 @@ import io.cloudevents.v03.ContextAttributes; /** - * + * * @author fabiojose * @version 0.3 */ @@ -46,12 +46,12 @@ private AttributeMapper() {} */ public static Map map(final Map headers) { Objects.requireNonNull(headers); - - final AtomicReference>> ct = + + final AtomicReference>> ct = new AtomicReference<>(); - + ct.set(Optional.empty()); - + Map result = headers.entrySet() .stream() .filter(header -> null!= header.getValue()) @@ -68,13 +68,13 @@ public static Map map(final Map headers) { .map(header -> new SimpleEntry<>(header.getKey(), header.getValue().toString())) .collect(toMap(Entry::getKey, Entry::getValue)); - + ct.get().ifPresent(contentType -> { - result.put(ContextAttributes.datacontenttype.name(), + result.put(ContextAttributes.DATACONTENTTYPE.name(), contentType.getValue().toString()); }); - + return result; } - + } diff --git a/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java b/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java index 82b2258ac..d073dae7a 100644 --- a/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java +++ b/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java @@ -29,13 +29,13 @@ import io.cloudevents.v03.ContextAttributes; /** - * + * * @author fabiojose * */ public class HeaderMapper { private HeaderMapper() {} - + private static final String HTTP_CONTENT_TYPE = "Content-Type"; /** @@ -48,31 +48,31 @@ public static Map map(Map attributes, Map extensions) { Objects.requireNonNull(attributes); Objects.requireNonNull(extensions); - + Map result = attributes.entrySet() .stream() .filter(attribute -> null!= attribute.getValue()) .map(header -> new SimpleEntry<>(header.getKey() .toLowerCase(Locale.US), header.getValue())) .filter(header -> !header.getKey() - .equals(ContextAttributes.datacontenttype.name())) + .equals(ContextAttributes.DATACONTENTTYPE.name())) .map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(), header.getValue())) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - + result.putAll( extensions.entrySet() .stream() .filter(extension -> null!= extension.getValue()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) ); - + Optional.ofNullable(attributes - .get(ContextAttributes.datacontenttype.name())) + .get(ContextAttributes.DATACONTENTTYPE.name())) .ifPresent((dct) -> { result.put(HTTP_CONTENT_TYPE, dct); }); - + return result; } diff --git a/api/src/main/java/io/cloudevents/v1/Accessor.java b/api/src/main/java/io/cloudevents/v1/Accessor.java deleted file mode 100644 index 044f2484d..000000000 --- a/api/src/main/java/io/cloudevents/v1/Accessor.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1; - -import java.util.Collection; -import java.util.Objects; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.ExtensionFormatAccessor; - -/** - * - * @author fabiojose - * @version 1.0 - */ -public class Accessor { - - /** - * To get access the set of {@link ExtensionFormat} inside the - * event. - * - *
- *
- * This method follow the signature of - * {@link ExtensionFormatAccessor#extensionsOf(CloudEvent)} - * - * @param cloudEvent - * @throws IllegalArgumentException When argument is not an instance - * of {@link CloudEventImpl} - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public static
Collection - extensionsOf(CloudEvent cloudEvent) { - Objects.requireNonNull(cloudEvent); - - if(cloudEvent instanceof CloudEventImpl) { - CloudEventImpl impl = (CloudEventImpl)cloudEvent; - return impl.getExtensionsFormats(); - } - - throw new IllegalArgumentException("Invalid instance type: " - + cloudEvent.getClass()); - } -} diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 8c51cae4e..2e603f469 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -18,6 +18,7 @@ import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; import java.net.URI; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -25,71 +26,55 @@ import java.util.Objects; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; - import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.cloudevents.Attributes; +import io.cloudevents.SpecVersion; import io.cloudevents.json.ZonedDateTimeDeserializer; /** - * + * * @author fabiojose + * @author slinkydeveloper * @version 1.0 */ -@JsonInclude(value = Include.NON_ABSENT) public class AttributesImpl implements Attributes { - - @NotBlank + private final String id; - - @NotNull + private final URI source; - - @NotBlank - @Pattern(regexp = "1\\.0") - private final String specversion; - - @NotBlank + private final String type; - + private final String datacontenttype; - + private final URI dataschema; - - @Size(min = 1) + private final String subject; - - @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + private final ZonedDateTime time; - public AttributesImpl(String id, URI source, String specversion, + public AttributesImpl(String id, URI source, String type, String datacontenttype, URI dataschema, String subject, ZonedDateTime time) { - + this.id = id; this.source = source; - this.specversion = specversion; this.type = type; this.datacontenttype = datacontenttype; this.dataschema = dataschema; this.subject = subject; this.time = time; } - + @Override - public Optional getMediaType() { - return getDatacontenttype(); + public Optional getDataContentType() { + return Optional.ofNullable(datacontenttype); } - public String getId() { + public String getId() { return id; } @@ -97,21 +82,18 @@ public URI getSource() { return source; } - public String getSpecversion() { - return specversion; + public SpecVersion getSpecVersion() { + return SpecVersion.V1; } public String getType() { return type; } - public Optional getDatacontenttype() { - return Optional.ofNullable(datacontenttype); - } - - public Optional getDataschema() { - return Optional.ofNullable(dataschema); - } + @Override + public Optional getDataSchema() { + return Optional.ofNullable(dataschema); + } public Optional getSubject() { return Optional.ofNullable(subject); @@ -123,13 +105,13 @@ public Optional getTime() { @Override public String toString() { - return "AttibutesImpl [id=" + id + ", source=" + source - + ", specversion=" + specversion + ", type=" + type - + ", datacontenttype=" + datacontenttype + ", dataschema=" + return "Attibutes V1.0 [id=" + id + ", source=" + source + + ", type=" + type + + ", datacontenttype=" + datacontenttype + ", dataschema=" + dataschema + ", subject=" + subject + ", time=" + time + "]"; } - + /** * Used by the Jackson framework to unmarshall. */ @@ -137,72 +119,69 @@ public String toString() { public static AttributesImpl build( @JsonProperty("id") String id, @JsonProperty("source") URI source, - @JsonProperty("specversion") String specversion, @JsonProperty("type") String type, @JsonProperty("datacontenttype") String datacontenttype, @JsonProperty("dataschema") URI dataschema, @JsonProperty("subject") String subject, @JsonProperty("time") ZonedDateTime time) { - - return new AttributesImpl(id, source, specversion, type, + + return new AttributesImpl(id, source, type, datacontenttype, dataschema, subject, time); } - + /** - * Creates the marshaller instance to marshall {@link AttributesImpl} as + * Creates the marshaller instance to marshall {@link AttributesImpl} as * a {@link Map} of strings */ public static Map marshal(AttributesImpl attributes) { Objects.requireNonNull(attributes); Map result = new HashMap<>(); - - result.put(ContextAttributes.id.name(), + + result.put(ContextAttributes.ID.name(), attributes.getId()); - result.put(ContextAttributes.source.name(), + result.put(ContextAttributes.SOURCE.name(), attributes.getSource().toString()); - result.put(ContextAttributes.specversion.name(), - attributes.getSpecversion()); - result.put(ContextAttributes.type.name(), + result.put(ContextAttributes.TYPE.name(), attributes.getType()); - - attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.datacontenttype.name(), dct)); - attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.dataschema.name(), + + attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.DATACONTENTTYPE.name(), dct)); + attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.DATASCHEMA.name(), dataschema.toString())); - attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.subject.name(), subject)); - attributes.getTime().ifPresent(time -> result.put(ContextAttributes.time.name(), + attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); + attributes.getTime().ifPresent(time -> result.put(ContextAttributes.TIME.name(), time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); - + return result; } - + /** * The attribute unmarshaller for the binary format, that receives a * {@code Map} with attributes names as String and value as String. */ public static AttributesImpl unmarshal(Map attributes) { - String type = attributes.get(ContextAttributes.type.name()); + String type = attributes.get(ContextAttributes.TYPE.name()); ZonedDateTime time = - Optional.ofNullable(attributes.get(ContextAttributes.time.name())) + Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) .map((t) -> ZonedDateTime.parse(t, ISO_ZONED_DATE_TIME)) .orElse(null); - - String specversion = attributes.get(ContextAttributes.specversion.name()); - URI source = URI.create(attributes.get(ContextAttributes.source.name())); - - URI dataschema = - Optional.ofNullable(attributes.get(ContextAttributes.dataschema.name())) + + String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); + URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); + + URI dataschema = + Optional.ofNullable(attributes.get(ContextAttributes.DATASCHEMA.name())) .map(URI::create) .orElse(null); - - String id = attributes.get(ContextAttributes.id.name()); - - String datacontenttype = - attributes.get(ContextAttributes.datacontenttype.name()); - - String subject = attributes.get(ContextAttributes.subject.name()); - - return AttributesImpl.build(id, source, specversion, type, + + String id = attributes.get(ContextAttributes.ID.name()); + + String datacontenttype = + attributes.get(ContextAttributes.DATACONTENTTYPE.name()); + + String subject = attributes.get(ContextAttributes.SUBJECT.name()); + + return AttributesImpl.build(id, source, type, datacontenttype, dataschema, subject, time); } } diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index 037576ed8..156db0a4c 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -2,39 +2,16 @@ import java.net.URI; import java.time.ZonedDateTime; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.EventBuilder; - -import static java.lang.String.format; +import io.cloudevents.common.BaseCloudEventBuilder; /** - * + * * @author fabiojose + * @author slinkydeveloper * @version 1.0 */ -public class CloudEventBuilder implements - EventBuilder { - - private CloudEventBuilder() {} - - private static Validator VALIDATOR; - - public static final String SPEC_VERSION = "1.0"; - private static final String MESSAGE_SEPARATOR = ", "; - private static final String MESSAGE = "'%s' %s"; - private static final String ERR_MESSAGE = "invalid payload: %s"; - +public final class CloudEventBuilder extends BaseCloudEventBuilder { + private String id; private URI source; @@ -44,173 +21,48 @@ private CloudEventBuilder() {} private String subject; private ZonedDateTime time; - private T data; - private byte[] dataBase64; - - private final Set extensions = new HashSet<>(); - - private Validator validator; - - private static Validator getValidator() { - if(null== VALIDATOR) { - VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); - } - return VALIDATOR; - } - - /** - * Gets a brand new builder instance - * @param The 'data' type - */ - public static CloudEventBuilder builder() { - return new CloudEventBuilder<>(); - } - - /** - * Builder with base event to copy attributes - * @param The 'data' type - * @param base The base event to copy attributes - */ - public static CloudEventBuilder builder( - CloudEvent base) { - Objects.requireNonNull(base); - - CloudEventBuilder result = new CloudEventBuilder<>(); - - AttributesImpl attributes = base.getAttributes(); - - result - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getDataschema().ifPresent(result::withDataschema); - attributes.getDatacontenttype().ifPresent(result::withDataContentType); - attributes.getSubject().ifPresent(result::withSubject); - attributes.getTime().ifPresent(result::withTime); - Accessor.extensionsOf(base).forEach(result::withExtension); - base.getData().ifPresent(result::withData); - if(base.getDataBase64() != null) { - result.withDataBase64(base.getDataBase64()); - } - return result; - } - - @Override - public CloudEvent build(T data, - AttributesImpl attributes, - Collection extensions) { - - CloudEventBuilder builder = CloudEventBuilder.builder() - .withId(attributes.getId()) - .withSource(attributes.getSource()) - .withType(attributes.getType()); - - attributes.getTime().ifPresent(builder::withTime); - attributes.getDataschema().ifPresent(builder::withDataschema); - attributes.getDatacontenttype().ifPresent(builder::withDataContentType); - attributes.getSubject().ifPresent(builder::withSubject); - extensions.forEach(builder::withExtension); - - return builder - .withData(data) - .withDataBase64(dataBase64) - .withValidator(validator) - .build(); - } - - /** - * - * @return An new {@link CloudEvent} immutable instance - * @throws IllegalStateException When there are specification constraints - * violations - */ - public CloudEventImpl build() { - - AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION, type, - datacontenttype, dataschema, subject, time); - - CloudEventImpl cloudEvent; - if(data != null) { - cloudEvent = new CloudEventImpl<>(attributes, data, extensions); - } else { - cloudEvent = new CloudEventImpl<>(attributes, dataBase64, extensions); - } - - if(validator == null) { - validator = getValidator(); - } - Set> violations = new HashSet<>(); - violations.addAll(validator.validate(cloudEvent)); - violations.addAll(validator.validate(cloudEvent.getAttributes())); - - final String errs = - violations.stream() - .map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage())) - .collect(Collectors.joining(MESSAGE_SEPARATOR)); - - if(!errs.trim().isEmpty()) { - throw new IllegalStateException(format(ERR_MESSAGE, errs)); - } - - return cloudEvent; - } - + public CloudEventBuilder() { + super(); + } - public CloudEventBuilder withId(String id) { + public CloudEventBuilder withId(String id) { this.id = id; return this; } - - public CloudEventBuilder withSource(URI source) { + + public CloudEventBuilder withSource(URI source) { this.source = source; return this; } - - public CloudEventBuilder withType(String type) { + + public CloudEventBuilder withType(String type) { this.type = type; return this; } - - public CloudEventBuilder withDataschema(URI dataschema) { + + public CloudEventBuilder withDataSchema(URI dataschema) { this.dataschema = dataschema; return this; } - - public CloudEventBuilder withDataContentType( + + public CloudEventBuilder withDataContentType( String datacontenttype) { this.datacontenttype = datacontenttype; return this; } - - public CloudEventBuilder withSubject( + + public CloudEventBuilder withSubject( String subject) { this.subject = subject; return this; } - - public CloudEventBuilder withTime(ZonedDateTime time) { - this.time = time; - return this; - } - - public CloudEventBuilder withData(T data) { - this.data = data; - return this; - } - - public CloudEventBuilder withDataBase64(byte[] dataBase64) { - this.dataBase64 = dataBase64; - return this; - } - public CloudEventBuilder withExtension(ExtensionFormat extension) { - this.extensions.add(extension); + public CloudEventBuilder withTime(ZonedDateTime time) { + this.time = time; return this; - } + } - public CloudEventBuilder withValidator(Validator validator) { - this.validator = validator; - return this; - } + protected AttributesImpl buildAttributes() { + return new AttributesImpl(id, source, type, datacontenttype, dataschema, subject, time); + } } diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java b/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java deleted file mode 100644 index c9eb1961e..000000000 --- a/api/src/main/java/io/cloudevents/v1/CloudEventImpl.java +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.validation.constraints.NotNull; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; - -/** - * - * @author fabiojose - * @version 1.0 - */ -@JsonInclude(value = JsonInclude.Include.NON_ABSENT) -public class CloudEventImpl implements CloudEvent { - - @NotNull - @JsonIgnore - private final AttributesImpl attributes; - - private final T data; - - //To use with json binary data - private final byte[] dataBase64; - - @NotNull - private final Map extensions; - - private final Set extensionsFormats; - - CloudEventImpl(AttributesImpl attributes, byte[] dataBase64, - Set extensions){ - this(attributes, extensions, null, dataBase64); - } - - CloudEventImpl(AttributesImpl attributes, T data, - Set extensions){ - this(attributes, extensions, data, null); - } - - private CloudEventImpl(AttributesImpl attributes, Set extensions, T data, byte[] dataBase64){ - this.attributes = attributes; - this.extensions = extensions.stream() - .map(ExtensionFormat::memory) - .collect(Collectors.toMap(InMemoryFormat::getKey, - InMemoryFormat::getValue)); - this.data = data; - this.dataBase64 = dataBase64; - this.extensionsFormats = extensions; - } - - /** - * Used by the {@link Accessor} to access the set of {@link ExtensionFormat} - */ - Set getExtensionsFormats() { - return extensionsFormats; - } - - - @Override - @JsonUnwrapped - public AttributesImpl getAttributes() { - return attributes; - } - - @Override - public Optional getData() { - return Optional.ofNullable(data); - } - - @Override - @JsonProperty("data_base64") - public byte[] getDataBase64() { - return dataBase64; - } - - @Override - @JsonAnyGetter - public Map getExtensions() { - return Collections.unmodifiableMap(extensions); - } - - /** - * The unique method that allows mutation. Used by - * Jackson Framework to inject the extensions. - * - * @param name Extension name - * @param value Extension value - */ - @JsonAnySetter - void addExtension(String name, Object value) { - extensions.put(name, value); - } - - /** - * Used by the Jackson Framework to unmarshall. - */ - @JsonCreator - public static CloudEventImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("type") String type, - @JsonProperty("datacontenttype") String datacontenttype, - @JsonProperty("dataschema") URI dataschema, - @JsonProperty("subject") String subject, - @JsonProperty("time") ZonedDateTime time, - @JsonProperty("data") T data, - @JsonProperty("data_base64") byte[] dataBase64){ - - return CloudEventBuilder.builder() - .withId(id) - .withSource(source) - .withType(type) - .withTime(time) - .withDataschema(dataschema) - .withDataContentType(datacontenttype) - .withData(data) - .withDataBase64(dataBase64) - .withSubject(subject) - .build(); - } -} diff --git a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java index 36407e83c..ee67a91a7 100644 --- a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java @@ -20,23 +20,22 @@ import java.util.stream.Collectors; /** - * + * * @author fabiojose * @version 1.0 */ public enum ContextAttributes { - - id, - source, - specversion, - type, - datacontenttype, - dataschema, - subject, - time; - - public static final List VALUES = + ID, + SOURCE, + SPECVERSION, + TYPE, + DATACONTENTTYPE, + DATASCHEMA, + SUBJECT, + TIME; + public static final List VALUES = Arrays.stream(ContextAttributes.values()) .map(Enum::name) + .map(String::toLowerCase) .collect(Collectors.toList()); } diff --git a/api/src/main/java/io/cloudevents/v1/http/AttributeMapper.java b/api/src/main/java/io/cloudevents/v1/http/AttributeMapper.java index 4841d5276..344b03ee8 100644 --- a/api/src/main/java/io/cloudevents/v1/http/AttributeMapper.java +++ b/api/src/main/java/io/cloudevents/v1/http/AttributeMapper.java @@ -29,13 +29,13 @@ import io.cloudevents.v1.ContextAttributes; /** - * + * * @author fabiojose * @version 1.0 */ public class AttributeMapper { private AttributeMapper() {} - + static final String HEADER_PREFIX = "ce-"; /** @@ -46,12 +46,12 @@ private AttributeMapper() {} */ public static Map map(final Map headers) { Objects.requireNonNull(headers); - - final AtomicReference>> ct = + + final AtomicReference>> ct = new AtomicReference<>(); - + ct.set(Optional.empty()); - + Map result = headers.entrySet() .stream() .filter(header -> null!= header.getValue()) @@ -68,12 +68,12 @@ public static Map map(final Map headers) { .map(header -> new SimpleEntry<>(header.getKey(), header.getValue().toString())) .collect(toMap(Entry::getKey, Entry::getValue)); - + ct.get().ifPresent(contentType -> { - result.put(ContextAttributes.datacontenttype.name(), + result.put(ContextAttributes.DATACONTENTTYPE.name(), contentType.getValue().toString()); }); - + return result; } diff --git a/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java b/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java index faece4a51..201ff8959 100644 --- a/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java +++ b/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java @@ -27,11 +27,9 @@ import io.cloudevents.fun.FormatHeaderMapper; import io.cloudevents.v1.ContextAttributes; -import io.cloudevents.v1.http.AttributeMapper; -import io.cloudevents.v1.http.ExtensionMapper; /** - * + * * @author fabiojose * */ @@ -50,31 +48,31 @@ public static Map map(Map attributes, Map extensions) { Objects.requireNonNull(attributes); Objects.requireNonNull(extensions); - + Map result = attributes.entrySet() .stream() .filter(attribute -> null!= attribute.getValue()) .map(header -> new SimpleEntry<>(header.getKey() .toLowerCase(Locale.US), header.getValue())) .filter(header -> !header.getKey() - .equals(ContextAttributes.datacontenttype.name())) + .equals(ContextAttributes.DATACONTENTTYPE.name())) .map(header -> new SimpleEntry<>(HEADER_PREFIX + header.getKey(), header.getValue())) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - + result.putAll( extensions.entrySet() .stream() .filter(extension -> null != extension.getValue()) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) ); - + Optional.ofNullable(attributes - .get(ContextAttributes.datacontenttype.name())) + .get(ContextAttributes.DATACONTENTTYPE.name())) .ifPresent((dct) -> { result.put(HTTP_CONTENT_TYPE, dct); }); - + return result; } diff --git a/api/src/test/java/io/cloudevents/v02/AccessorTest.java b/api/src/test/java/io/cloudevents/v02/AccessorTest.java deleted file mode 100644 index 6b994bac3..000000000 --- a/api/src/test/java/io/cloudevents/v02/AccessorTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.Collection; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; - -/** - * - * @author fabiojose - * - */ -public class AccessorTest { - - @Test - public void should_empty_collection_when_no_extensions() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .build(); - - // act - Collection actual = Accessor.extensionsOf(ce); - - // assert - assertTrue(actual.isEmpty()); - } - - @Test - public void should_return_the_tracing_extension() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat expected = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals("0", actual.transport().get("traceparent")); - assertEquals("congo=4", actual.transport().get("tracestate")); - - assertEquals("0", - ((DistributedTracingExtension)actual.memory().getValue()).getTraceparent()); - - assertEquals("congo=4", - ((DistributedTracingExtension)actual.memory().getValue()).getTracestate()); - } - - @Test - public void should_return_the_custom_extension() { - // setup - String customExt = "comexampleextension1"; - String customVal = "my-ext-val"; - InMemoryFormat inMemory = - InMemoryFormat.of(customExt, customVal, String.class); - - ExtensionFormat expected = - ExtensionFormat.of(inMemory, customExt, customVal); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals(customVal, actual.transport().get(customExt)); - - assertEquals(String.class, actual.memory().getValueType()); - - assertEquals(customExt, actual.memory().getKey()); - - assertEquals(customVal, actual.memory().getValue()); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/CloudEventBuilderTest.java b/api/src/test/java/io/cloudevents/v02/CloudEventBuilderTest.java deleted file mode 100644 index 8b39fedb8..000000000 --- a/api/src/test/java/io/cloudevents/v02/CloudEventBuilderTest.java +++ /dev/null @@ -1,361 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Collections; - -import javax.validation.Validator; - -import io.cloudevents.validation.MockValidator; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; -import io.cloudevents.json.types.Much; - -/** - * - * @author fabiojose - * - */ -public class CloudEventBuilderTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_null_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - CloudEventBuilder.builder() - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_empty_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("") - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_null_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/test")) - .build(); - } - - @Test - public void error_when_empty_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/test")) - .withType("") - .build(); - } - - @Test - public void error_when_null_source() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'source' must not be null"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withType("type") - .build(); - } - - @Test - public void should_have_id() { - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .build(); - - // assert - assertEquals("id", ce.getAttributes().getId()); - } - - @Test - public void should_have_source() { - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .build(); - - // assert - assertEquals(URI.create("/source"), ce.getAttributes().getSource()); - } - - @Test - public void should_have_type() { - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .build(); - - // assert - assertEquals("type", ce.getAttributes().getType()); - } - - @Test - public void should_have_specversion() { - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .build(); - - // assert - assertEquals("0.2", ce.getAttributes().getSpecversion()); - } - - @Test - public void should_have_time() { - // setup - ZonedDateTime expected = ZonedDateTime.now(); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withTime(expected) - .build(); - - // assert - assertTrue(ce.getAttributes().getTime().isPresent()); - assertEquals(expected, ce.getAttributes().getTime().get()); - } - - @Test - public void should_have_schemaurl() { - // setup - URI expected = URI.create("/schema"); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withSchemaurl(expected) - .build(); - - // assert - assertTrue(ce.getAttributes().getSchemaurl().isPresent()); - assertEquals(expected, ce.getAttributes().getSchemaurl().get()); - } - - @Test - public void should_have_contenttype() { - // setup - String expected = "application/json"; - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withContenttype(expected) - .build(); - - // assert - assertTrue(ce.getAttributes().getContenttype().isPresent()); - assertEquals(expected, ce.getAttributes().getContenttype().get()); - } - - @Test - public void should_have_data() { - // setup - String expected = "my data"; - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withData(expected) - .build(); - - // assert - assertTrue(ce.getData().isPresent()); - assertEquals(expected, ce.getData().get()); - } - - @Test - public void should_have_dte() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - Object actual = ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - - // assert - assertNotNull(actual); - assertTrue(actual instanceof DistributedTracingExtension); - } - - @Test - public void should_have_custom_extension() { - String myExtKey = "comexampleextension1"; - String myExtVal = "value"; - - ExtensionFormat custom = ExtensionFormat - .of(InMemoryFormat.of(myExtKey, myExtKey, String.class), - myExtKey, myExtVal); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(custom) - .build(); - - Object actual = ce.getExtensions() - .get(myExtKey); - - assertNotNull(actual); - assertTrue(actual instanceof String); - } - - @Test - public void should_builder_change_data_and_id() { - // setup - Much data = new Much(); - data.setWow("amzing"); - - String expected = "amazing"; - - - CloudEventImpl base = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withData(data) - .build(); - - // act - CloudEvent actual = - CloudEventBuilder.builder().build(base, "0x010", expected); - - // assert - assertTrue(actual.getData().isPresent()); - assertEquals(expected, actual.getData().get()); - assertEquals("0x010", actual.getAttributes().getId()); - } - - @Test - public void should_build_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - - CloudEventImpl event = CloudEventBuilder - .builder() - .withData(expected) - .withValidator(validator) - .build(); - - assertNotNull(event); - assertEquals(expected, event.getData().get()); - } - - @Test - public void should_build_event_from_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - CloudEvent event = CloudEventBuilder.of( - expected, - new AttributesImpl(null, null, null, null, null, null, null), - Collections.emptyList(), - validator - ); - - CloudEvent result = CloudEventBuilder - .builder(event) - .withValidator(validator) - .build(); - - assertNotNull(result); - assertEquals(expected, result.getData().get()); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v02/CloudEventJacksonTest.java deleted file mode 100644 index ee3235890..000000000 --- a/api/src/test/java/io/cloudevents/v02/CloudEventJacksonTest.java +++ /dev/null @@ -1,232 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.InputStream; -import java.net.URI; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.json.Json; - -/** - * - * @author fabiojose - * - */ -public class CloudEventJacksonTest { - - private static InputStream resourceOf(String name) { - return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); - } - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void should_encode_right_with_minimal_attrs() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("x10")); - assertTrue(json.contains("/source")); - assertTrue(json.contains("event-type")); - assertTrue(json.contains("0.2")); - - assertFalse(json.contains("time")); - assertFalse(json.contains("schemaurl")); - assertFalse(json.contains("contenttype")); - assertFalse(json.contains("data")); - } - - @Test - public void should_have_optional_attrs() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("/schema")); - assertTrue(json.contains("text/plain")); - assertTrue(json.contains("my-data")); - } - - @Test - public void should_serialize_trace_extension() { - // setup - String expected = "\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}"; - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .withExtension(tracing) - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertTrue(actual.contains(expected)); - } - - @Test - public void should_not_serialize_attributes_element() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withContenttype("text/plain") - .withData("my-data") - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertFalse(actual.contains("\"attributes\"")); - } - - @Test - public void should_have_type() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class); - - // assert - assertEquals("aws.s3.object.created", ce.getAttributes().getType()); - } - - @Test - public void should_have_id() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class); - - // assert - assertEquals("C234-1234-1234", ce.getAttributes().getId()); - } - - //should have time - @Test - public void should_have_time() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getTime().isPresent()); - } - - @Test - public void should_have_source() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class); - - // assert - assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource()); - } - - @Test - public void should_have_contenttype() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_aws.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getContenttype().isPresent()); - assertEquals("application/json", ce.getAttributes().getContenttype().get()); - } - - @Test - public void should_have_specversion() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class); - - // assert - assertEquals("0.2", ce.getAttributes().getSpecversion()); - } - - @Test - public void should_throw_when_absent() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - Json.fromInputStream(resourceOf("02_absent.json"), CloudEventImpl.class); - } - - @Test - public void should_have_tracing_extension() { - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEventImpl.class); - - // assert - assertNotNull(ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - } - - @Test - public void should_have_custom_extension() { - // setup - String extensionKey = "my-extension"; - String expected = "extension-value"; - - // act - CloudEventImpl ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEventImpl.class); - - // assert - assertEquals(expected, ce.getExtensions() - .get(extensionKey)); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/AttributeMapperTest.java b/api/src/test/java/io/cloudevents/v02/http/AttributeMapperTest.java deleted file mode 100644 index 1235e383f..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/AttributeMapperTest.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * - * @author fabiojose - * - */ -public class AttributeMapperTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - AttributeMapper.map(null); - } - - @Test - public void should_not_map_null_header_value_to_attribute() { - // setup - Map headers = new HashMap<>(); - headers.put("ce_specversion", null); - - // act - Map actual = AttributeMapper.map(headers); - - // assert - assertFalse(actual.containsKey("specversion")); - } - - @Test - public void should_ok_when_no_content_type() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "0.2"); - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.isEmpty()); - } - - @Test - public void should_map_cespecversion_to_specversion() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "0.2"); - headers.put("Content-Type", "application/json"); - - String expected = "specversion"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertNotNull(attributes.get(expected)); - } - - @Test - public void should_not_map_null_value() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-type", null); - - String expected = "type"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.containsKey(expected)); - } - - @Test - public void should_all_without_prefix_ce() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - Map actual = AttributeMapper.map(myHeaders); - - actual.keySet() - .forEach((attribute) -> { - assertFalse(attribute.startsWith("ce-")); - }); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/ExtensionMapperTest.java b/api/src/test/java/io/cloudevents/v02/http/ExtensionMapperTest.java deleted file mode 100644 index aae0624b3..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/ExtensionMapperTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * - * @author fabiojose - * - */ -public class ExtensionMapperTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - ExtensionMapper.map(null); - } - - @Test - public void should_not_map_null_values() { - //setuṕ - String expected = "nullexp"; - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - myHeaders.put(expected, null); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - - // assert - assertFalse(actual.containsKey(expected)); - } - - @Test - public void should_return_just_potential_extensions() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - // asset - assertFalse(actual.isEmpty()); - assertEquals(3, actual.keySet().size()); - actual.keySet() - .forEach(header -> { - assertFalse(header.startsWith("ce-")); - }); - - assertEquals("0", actual.get("traceparent")); - assertEquals("congo=4", actual.get("tracestate")); - assertEquals("myextension", actual.get("my-ext")); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryMarshallerTest.java b/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryMarshallerTest.java deleted file mode 100644 index 77029c901..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryMarshallerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryMarshallerTest { - - @Test - public void should_marshal_data_as_json() { - // setup - String expected = "{\"wow\":\"yes!\"}"; - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("application/json") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_attributes_as_headers() { - // setup - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("application/json") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertEquals(ce.getAttributes().getId(), actual.getHeaders().get("ce-id")); - assertEquals(ce.getAttributes().getSource(), URI.create(actual.getHeaders().get("ce-source"))); - assertEquals(ce.getAttributes().getType(), actual.getHeaders().get("ce-type")); - assertEquals(ce.getAttributes().getContenttype().get(), actual.getHeaders().get("Content-Type")); - } - - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryUnmarshallerTest.java deleted file mode 100644 index 30e01d8a9..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/HTTPBinaryUnmarshallerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v02.AttributesImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryUnmarshallerTest { - - @Test - public void should_unmarshal_headers_and_json_payload() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertEquals("0x11", actual.getAttributes().getId()); - assertEquals(URI.create("/source"), actual.getAttributes().getSource()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); - assertEquals("br.my", actual.getAttributes().getType()); - assertTrue(actual.getAttributes().getTime().isPresent()); - assertTrue(actual.getAttributes().getSchemaurl().isPresent()); - assertEquals(URI.create("http://my.br"), actual.getAttributes().getSchemaurl().get()); - assertTrue(actual.getAttributes().getContenttype().isPresent()); - assertEquals("application/json", actual.getAttributes().getContenttype().get()); - assertTrue(actual.getData().isPresent()); - assertEquals(expected, actual.getData().get()); - } - - @Test - public void should_unmarshal_tracing_extension_from_header() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - myHeaders.put("traceparent", "0x200"); - myHeaders.put("tracestate", "congo=9"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertNotNull(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - assertTrue(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredMarshallerTest.java b/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredMarshallerTest.java deleted file mode 100644 index 9cfcb6321..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredMarshallerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredMarshallerTest { - - @Test - public void should_marshal_all_as_json() { - // setup - String expected = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("application/json") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_data_as_text_and_evelope_as_json() { - // setup - String expected = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_headers_have_content_type() { - // setup - String expected = "application/cloudevents+json"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertTrue(actual.getHeaders().containsKey("Content-Type")); - assertEquals(expected, actual.getHeaders().get("Content-Type")); - } - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredUnmasharllerTest.java b/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredUnmasharllerTest.java deleted file mode 100644 index 3355964c8..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/HTTPStructuredUnmasharllerTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v02.AttributesImpl; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredUnmasharllerTest { - - @Test - public void should_unmarshal_json_envelope_and_json_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("application/json") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(Much.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - } - - @Test - public void should_unmarshal_json_envelope_and_text_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withContenttype("text/plain") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - } - - @Test - public void should_unmarshal_the_tracing_extension_from_headers() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - httpHeaders.put("traceparent", "0x200"); - httpHeaders.put("tracestate", "congo=9"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"text/plain\"}"; - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertTrue(actual.getExtensions().containsKey( - DistributedTracingExtension.Format.IN_MEMORY_KEY)); - - assertTrue(actual.getExtensions().get( - DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v02/http/HeaderMapperTest.java b/api/src/test/java/io/cloudevents/v02/http/HeaderMapperTest.java deleted file mode 100644 index eb343ae9b..000000000 --- a/api/src/test/java/io/cloudevents/v02/http/HeaderMapperTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.http; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapperTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_attributes_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map extensions = new HashMap<>(); - - // act - HeaderMapper.map(null, extensions); - } - - @Test - public void error_when_extensions_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map attributes = new HashMap<>(); - - // act - HeaderMapper.map(attributes, null); - } - - @Test - public void should_not_map_null_attribute_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", null); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("ce-type")); - } - - @Test - public void should_not_map_null_extension_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", null); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("null-ext")); - } - - @Test - public void should_not_map_absent_contenttype() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", "null-value"); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("Content-Type")); - } - - @Test - public void should_map_extension_without_prefix() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", "null-value"); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertTrue(actual.containsKey("comexampleextension1")); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java index 92e82d02d..a315326df 100644 --- a/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java +++ b/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java @@ -36,7 +36,7 @@ import io.cloudevents.json.types.Much; /** - * + * * @author fabiojose * */ @@ -45,39 +45,39 @@ public class CloudEventJacksonTest { private static InputStream resourceOf(String name) { return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); } - + @Rule public ExpectedException expectedEx = ExpectedException.none(); - + @Test public void should_encode_right_with_minimal_attrs() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") .build(); - + // act String json = Json.encode(ce); - + // assert assertTrue(json.contains("x10")); assertTrue(json.contains("/source")); assertTrue(json.contains("event-type")); assertTrue(json.contains("0.3")); - + assertFalse(json.contains("time")); assertFalse(json.contains("schemaurl")); assertFalse(json.contains("contenttype")); assertFalse(json.contains("data")); } - + @Test public void should_have_optional_attrs() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -88,23 +88,23 @@ public void should_have_optional_attrs() { .withSubject("subject0") .withData("my-data") .build(); - + // act String json = Json.encode(ce); - + // assert assertTrue(json.contains("/schema")); assertTrue(json.contains("text/plain")); assertTrue(json.contains("my-data")); assertTrue(json.contains("\"base64\"")); assertTrue(json.contains("subject0")); - + assertTrue(json.contains("\"schemaurl\"")); assertTrue(json.contains("datacontenttype")); assertTrue(json.contains("datacontentencoding")); assertTrue(json.contains("\"subject\"")); } - + @Test public void should_serialize_trace_extension() { // setup @@ -112,10 +112,10 @@ public void should_serialize_trace_extension() { final DistributedTracingExtension dt = new DistributedTracingExtension(); dt.setTraceparent("0"); dt.setTracestate("congo=4"); - + final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEvent ce = + + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -125,18 +125,18 @@ public void should_serialize_trace_extension() { .withData("my-data") .withExtension(tracing) .build(); - + // act String actual = Json.encode(ce); - + // assert assertTrue(actual.contains(expected)); } - + @Test public void should_not_serialize_attributes_element() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -146,138 +146,138 @@ public void should_not_serialize_attributes_element() { .withSubject("subject0") .withData("my-data") .build(); - + // act String actual = Json.encode(ce); - + // assert assertFalse(actual.contains("\"attributes\"")); } - + @Test public void should_have_type() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); // assert assertEquals("aws.s3.object.created", ce.getAttributes().getType()); } - + @Test public void should_have_id() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - + // assert assertEquals("C234-1234-1234", ce.getAttributes().getId()); } - + //should have time @Test public void should_have_time() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getTime().isPresent()); } - + @Test public void should_have_source() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - + // assert assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource()); } - + @Test public void should_have_datacontenttype() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", ce.getAttributes().getDatacontenttype().get()); } - + @Test public void should_have_datacontentencoding() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_base64.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getDatacontentencoding().isPresent()); assertEquals("base64", ce.getAttributes().getDatacontentencoding().get()); } - + @Test public void should_have_specversion() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - + // assert - assertEquals("0.3", ce.getAttributes().getSpecversion()); + assertEquals("0.3", ce.getAttributes().getSpecVersion()); } - + @Test public void should_throw_when_absent() { // setup expectedEx.expect(IllegalStateException.class); expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - + // act Json.fromInputStream(resourceOf("03_absent.json"), CloudEventImpl.class); } - + @Test public void should_have_tracing_extension() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_extension.json"), CloudEventImpl.class); - + // assert assertNotNull(ce.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); } - + @Test public void should_have_custom_extension() { // setup String extensionKey = "my-extension"; String expected = "extension-value"; - + // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("03_extension.json"), CloudEventImpl.class); - + // assert assertEquals(expected, ce.getExtensions() .get(extensionKey)); } - + @Test public void should_have_custom_data() { // setup Much expected = new Much(); expected.setWow("kinda"); - + String json = "{\"type\":\"aws.s3.object.created\",\"id\":\"C234-1234-1234\",\"time\":\"2019-08-19T19:35:00.000Z\",\"source\":\"https://serverless.com\",\"datacontenttype\":\"application/json\",\"specversion\":\"0.3\",\"data\":{\"wow\":\"kinda\"}}"; - - // act - CloudEvent ce = + + // act + CloudEvent ce = Json.decodeValue(json, new TypeReference>() {}); - + // assert assertTrue(ce.getData().isPresent()); assertEquals(expected.getWow(), ce.getData().get().getWow()); } - + } diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java index aa6983707..32d1bd7b1 100644 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java +++ b/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java @@ -31,7 +31,7 @@ import io.cloudevents.v03.AttributesImpl; /** - * + * * @author fabiojose * */ @@ -41,7 +41,7 @@ public void should_unmarshal_headers_and_json_payload() { // setup Much expected = new Much(); expected.setWow("yes!"); - + Map myHeaders = new HashMap<>(); myHeaders.put("ce-id", "0x11"); myHeaders.put("ce-source", "/source"); @@ -51,20 +51,20 @@ public void should_unmarshal_headers_and_json_payload() { myHeaders.put("ce-schemaurl", "http://my.br"); myHeaders.put("ce-subject", "subject"); myHeaders.put("Content-Type", "application/json"); - + String payload = "{\"wow\":\"yes!\"}"; - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.binary(Much.class) .withHeaders(() -> myHeaders) .withPayload(() -> payload) .unmarshal(); - + // assert assertEquals("0x11", actual.getAttributes().getId()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals("br.my", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getTime().isPresent()); assertTrue(actual.getAttributes().getSchemaurl().isPresent()); @@ -76,13 +76,13 @@ public void should_unmarshal_headers_and_json_payload() { assertTrue(actual.getAttributes().getSubject().isPresent()); assertEquals("subject", actual.getAttributes().getSubject().get()); } - + @Test public void should_unmarshal_tracing_extension_from_header() { // setup Much expected = new Much(); expected.setWow("yes!"); - + Map myHeaders = new HashMap<>(); myHeaders.put("ce-id", "0x11"); myHeaders.put("ce-source", "/source"); @@ -91,24 +91,24 @@ public void should_unmarshal_tracing_extension_from_header() { myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); myHeaders.put("ce-schemaurl", "http://my.br"); myHeaders.put("Content-Type", "application/json"); - + myHeaders.put("traceparent", "0x200"); myHeaders.put("tracestate", "congo=9"); - + String payload = "{\"wow\":\"yes!\"}"; - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.binary(Much.class) .withHeaders(() -> myHeaders) .withPayload(() -> payload) .unmarshal(); - + // assert assertNotNull(actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); assertTrue(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) + .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) instanceof DistributedTracingExtension); } } diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java b/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java index 8d6944a42..b40615d14 100644 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java +++ b/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java @@ -32,7 +32,7 @@ import io.cloudevents.v03.CloudEventImpl; /** - * + * * @author fabiojose * */ @@ -42,13 +42,13 @@ public void should_unmarshal_json_envelope_and_json_data() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String json = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - + Much ceData = new Much(); ceData.setWow("yes!"); - - CloudEventImpl expected = + + CloudEventImpl expected = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -57,45 +57,45 @@ public void should_unmarshal_json_envelope_and_json_data() { .withSubject("subject") .withData(ceData) .build(); - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(Much.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - + assertEquals(expected.getAttributes().getSpecVersion(), + actual.getAttributes().getSpecVersion()); + assertEquals(expected.getAttributes().getId(), actual.getAttributes().getId()); - + assertEquals(expected.getAttributes().getSource(), actual.getAttributes().getSource()); - + assertEquals(expected.getAttributes().getType(), actual.getAttributes().getType()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected.getData().get(), actual.getData().get()); - + assertTrue(actual.getAttributes().getSubject().isPresent()); assertEquals(expected.getAttributes().getSubject().get(), actual.getAttributes().getSubject().get()); } - + @Test public void should_unmarshal_json_envelope_and_text_data() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; String ceData = "yes!"; - CloudEventImpl expected = + CloudEventImpl expected = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -103,55 +103,55 @@ public void should_unmarshal_json_envelope_and_text_data() { .withDatacontenttype("text/plain") .withData(ceData) .build(); - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(String.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - + assertEquals(expected.getAttributes().getSpecVersion(), + actual.getAttributes().getSpecVersion()); + assertEquals(expected.getAttributes().getId(), actual.getAttributes().getId()); - + assertEquals(expected.getAttributes().getSource(), actual.getAttributes().getSource()); - + assertEquals(expected.getAttributes().getType(), actual.getAttributes().getType()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected.getData().get(), actual.getData().get()); } - + @Test public void should_unmarshal_the_tracing_extension_from_headers() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + httpHeaders.put("traceparent", "0x200"); httpHeaders.put("tracestate", "congo=9"); - + String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(String.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert assertTrue(actual.getExtensions().containsKey( DistributedTracingExtension.Format.IN_MEMORY_KEY)); - + assertTrue(actual.getExtensions().get( - DistributedTracingExtension.Format.IN_MEMORY_KEY) + DistributedTracingExtension.Format.IN_MEMORY_KEY) instanceof DistributedTracingExtension); } } diff --git a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java index d239ae853..804645082 100644 --- a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java +++ b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java @@ -39,7 +39,7 @@ import static org.junit.Assert.assertTrue; /** - * + * * @author fabiojose * */ @@ -48,20 +48,20 @@ public class CloudEventJacksonTest { private static InputStream resourceOf(String name) { return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); } - + @Rule public ExpectedException expectedEx = ExpectedException.none(); - + @Test public void should_encode_right_with_minimal_attrs() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") .build(); - + // act String json = Json.encode(ce); @@ -70,17 +70,17 @@ public void should_encode_right_with_minimal_attrs() { assertTrue(json.contains("/source")); assertTrue(json.contains("event-type")); assertTrue(json.contains("1.0")); - + assertFalse(json.contains("time")); assertFalse(json.contains("schemaurl")); assertFalse(json.contains("contenttype")); - assertFalse(json.contains("data")); + assertFalse(json.contains("data")); } - + @Test public void should_have_optional_attrs() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -90,20 +90,20 @@ public void should_have_optional_attrs() { .withSubject("subject0") .withData("my-data") .build(); - + // act String json = Json.encode(ce); - + // assert assertTrue(json.contains("/schema")); assertTrue(json.contains("text/plain")); assertTrue(json.contains("my-data")); assertTrue(json.contains("subject0")); - + assertTrue(json.contains("\"dataschema\"")); assertTrue(json.contains("datacontenttype")); assertTrue(json.contains("\"subject\"")); - + Pattern pat = Pattern.compile("(\"data\")"); Matcher mat = pat.matcher(json); int counter = 0; @@ -112,7 +112,7 @@ public void should_have_optional_attrs() { } assertEquals(1, counter); } - + @Test public void should_serialize_trace_extension() { // setup @@ -120,10 +120,10 @@ public void should_serialize_trace_extension() { final DistributedTracingExtension dt = new DistributedTracingExtension(); dt.setTraceparent("0"); dt.setTracestate("congo=4"); - + final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEvent ce = + + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -133,18 +133,18 @@ public void should_serialize_trace_extension() { .withData("my-data") .withExtension(tracing) .build(); - + // act String actual = Json.encode(ce); - + // assert assertTrue(actual.contains(expected)); } - + @Test public void should_not_serialize_attributes_element() { // setup - CloudEvent ce = + CloudEvent ce = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -154,155 +154,155 @@ public void should_not_serialize_attributes_element() { .withSubject("subject0") .withData("my-data") .build(); - + // act String actual = Json.encode(ce); - + // assert assertFalse(actual.contains("\"attributes\"")); } - + @Test public void should_have_type() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert assertEquals("aws.s3.object.created", ce.getAttributes().getType()); } - + @Test public void should_have_id() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert assertEquals("C234-1234-1234", ce.getAttributes().getId()); } - + //should have time @Test public void should_have_time() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getTime().isPresent()); } - + @Test public void should_have_source() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource()); } - + @Test public void should_have_datacontenttype() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", ce.getAttributes().getDatacontenttype().get()); } - + @Test public void should_have_dataschema() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert assertTrue(ce.getAttributes().getDataschema().isPresent()); assertEquals(URI.create("/my-schema"), ce.getAttributes().getDataschema().get()); } - + @Test public void should_have_specversion() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - + // assert - assertEquals("1.0", ce.getAttributes().getSpecversion()); + assertEquals("1.0", ce.getAttributes().getSpecVersion()); } - + @Test public void should_throw_when_absent() { // setup expectedEx.expect(IllegalStateException.class); expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - + // act Json.fromInputStream(resourceOf("1_absent.json"), CloudEventImpl.class); } - + @Test public void should_have_tracing_extension() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); - + // assert assertNotNull(ce.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); } - + @Test public void should_have_custom_extension() { // setup String extensionKey = "my-extension"; String expected = "extension-value"; - + // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); - + // assert assertEquals(expected, ce.getExtensions() .get(extensionKey)); } - + @Test public void should_have_custom_data() { // setup Much expected = new Much(); expected.setWow("kinda"); - + String json = "{\"type\":\"aws.s3.object.created\",\"id\":\"C234-1234-1234\",\"time\":\"2019-08-19T19:35:00.000Z\",\"source\":\"https://serverless.com\",\"datacontenttype\":\"application/json\",\"specversion\":\"1.0\",\"data\":{\"wow\":\"kinda\"}}"; - - // act - CloudEvent ce = + + // act + CloudEvent ce = Json.decodeValue(json, new TypeReference>() {}); - + // assert assertTrue(ce.getData().isPresent()); assertEquals(expected.getWow(), ce.getData().get().getWow()); } - + @Test public void should_unmarshal_data_base64() { // setup byte[] expected = "mydata".getBytes(); - + // act CloudEvent ce = - Json.fromInputStream(resourceOf("1_base64.json"), + Json.fromInputStream(resourceOf("1_base64.json"), new TypeReference>() {}); // assert assertNotNull(ce.getDataBase64()); assertArrayEquals(expected, ce.getDataBase64()); } - + @Test public void should_marshal_data_byte_array_as_data_base64() { // setup @@ -313,10 +313,10 @@ public void should_marshal_data_byte_array_as_data_base64() { + "invoice=5566" + "\n" + "---mydata---").getBytes(); - - String expected = + + String expected = Base64.getEncoder().encodeToString(data); - + CloudEventImpl event = CloudEventBuilder.builder() .withId("0xbin") @@ -331,10 +331,10 @@ public void should_marshal_data_byte_array_as_data_base64() { // act String encoded = Json.encode(event); - + // assert assertTrue(encoded.contains("\"data_base64\"")); assertTrue(encoded.contains("\"" + expected +"\"")); } - + } diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java index 357191de9..fb0a27d14 100644 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java +++ b/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java @@ -30,7 +30,7 @@ import static org.junit.Assert.assertTrue; /** - * + * * @author fabiojose * */ @@ -40,7 +40,7 @@ public void should_unmarshal_headers_and_json_payload() { // setup Much expected = new Much(); expected.setWow("yes!"); - + Map myHeaders = new HashMap<>(); myHeaders.put("ce-id", "0x11"); myHeaders.put("ce-source", "/source"); @@ -50,20 +50,20 @@ public void should_unmarshal_headers_and_json_payload() { myHeaders.put("ce-dataschema", "http://my.br"); myHeaders.put("ce-subject", "subject"); myHeaders.put("Content-Type", "application/json"); - + String payload = "{\"wow\":\"yes!\"}"; - + // act CloudEvent actual = Unmarshallers.binary(Much.class) .withHeaders(() -> myHeaders) .withPayload(() -> payload) .unmarshal(); - + // assert assertEquals("0x11", actual.getAttributes().getId()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); - assertEquals("1.0", actual.getAttributes().getSpecversion()); + assertEquals("1.0", actual.getAttributes().getSpecVersion()); assertEquals("br.my", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getTime().isPresent()); assertTrue(actual.getAttributes().getDataschema().isPresent()); @@ -75,13 +75,13 @@ public void should_unmarshal_headers_and_json_payload() { assertTrue(actual.getAttributes().getSubject().isPresent()); assertEquals("subject", actual.getAttributes().getSubject().get()); } - + @Test public void should_unmarshal_tracing_extension_from_header() { // setup Much expected = new Much(); expected.setWow("yes!"); - + Map myHeaders = new HashMap<>(); myHeaders.put("ce-id", "0x11"); myHeaders.put("ce-source", "/source"); @@ -90,24 +90,24 @@ public void should_unmarshal_tracing_extension_from_header() { myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); myHeaders.put("ce-schemaurl", "http://my.br"); myHeaders.put("Content-Type", "application/json"); - + myHeaders.put("traceparent", "0x200"); myHeaders.put("tracestate", "congo=9"); - + String payload = "{\"wow\":\"yes!\"}"; - + // act CloudEvent actual = Unmarshallers.binary(Much.class) .withHeaders(() -> myHeaders) .withPayload(() -> payload) .unmarshal(); - + // assert assertNotNull(actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); assertTrue(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) + .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) instanceof DistributedTracingExtension); } } diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java index 0d5a21a96..c80bda991 100644 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java +++ b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java @@ -33,10 +33,9 @@ import io.cloudevents.v1.AttributesImpl; import io.cloudevents.v1.CloudEventBuilder; import io.cloudevents.v1.CloudEventImpl; -import io.cloudevents.v1.http.Unmarshallers; /** - * + * * @author fabiojose * */ @@ -46,13 +45,13 @@ public void should_unmarshal_json_envelope_and_json_data() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String json = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - + Much ceData = new Much(); ceData.setWow("yes!"); - - CloudEventImpl expected = + + CloudEventImpl expected = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -61,45 +60,45 @@ public void should_unmarshal_json_envelope_and_json_data() { .withSubject("subject") .withData(ceData) .build(); - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(Much.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - + assertEquals(expected.getAttributes().getSpecversion(), + actual.getAttributes().getSpecVersion()); + assertEquals(expected.getAttributes().getId(), actual.getAttributes().getId()); - + assertEquals(expected.getAttributes().getSource(), actual.getAttributes().getSource()); - + assertEquals(expected.getAttributes().getType(), actual.getAttributes().getType()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected.getData().get(), actual.getData().get()); - + assertTrue(actual.getAttributes().getSubject().isPresent()); assertEquals(expected.getAttributes().getSubject().get(), actual.getAttributes().getSubject().get()); } - + @Test public void should_unmarshal_json_envelope_and_text_data() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; String ceData = "yes!"; - CloudEventImpl expected = + CloudEventImpl expected = CloudEventBuilder.builder() .withId("x10") .withSource(URI.create("/source")) @@ -107,27 +106,27 @@ public void should_unmarshal_json_envelope_and_text_data() { .withDataContentType("text/plain") .withData(ceData) .build(); - + // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(String.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); - + assertEquals(expected.getAttributes().getSpecversion(), + actual.getAttributes().getSpecVersion()); + assertEquals(expected.getAttributes().getId(), actual.getAttributes().getId()); - + assertEquals(expected.getAttributes().getSource(), actual.getAttributes().getSource()); - + assertEquals(expected.getAttributes().getType(), actual.getAttributes().getType()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected.getData().get(), actual.getData().get()); } @@ -160,7 +159,7 @@ public void should_unmarshal_json_envelope_and_text_data_base64() { // assert assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecversion()); + actual.getAttributes().getSpecVersion()); assertEquals(expected.getAttributes().getId(), actual.getAttributes().getId()); @@ -181,25 +180,25 @@ public void should_unmarshal_the_tracing_extension_from_headers() { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + httpHeaders.put("traceparent", "0x200"); httpHeaders.put("tracestate", "congo=9"); - + String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; // act - CloudEvent actual = + CloudEvent actual = Unmarshallers.structured(String.class) .withHeaders(() -> httpHeaders) .withPayload(() -> json) .unmarshal(); - + // assert assertTrue(actual.getExtensions().containsKey( DistributedTracingExtension.Format.IN_MEMORY_KEY)); - + assertTrue(actual.getExtensions().get( - DistributedTracingExtension.Format.IN_MEMORY_KEY) + DistributedTracingExtension.Format.IN_MEMORY_KEY) instanceof DistributedTracingExtension); } } diff --git a/kafka/src/main/java/io/cloudevents/v02/kafka/AttributeMapper.java b/kafka/src/main/java/io/cloudevents/v02/kafka/AttributeMapper.java deleted file mode 100644 index ca39526f5..000000000 --- a/kafka/src/main/java/io/cloudevents/v02/kafka/AttributeMapper.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.kafka; - -import static java.util.stream.Collectors.toMap; - -import java.util.Locale; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; - -import org.apache.kafka.common.serialization.Deserializer; -import org.apache.kafka.common.serialization.Serdes; - -import io.cloudevents.fun.BinaryFormatAttributeMapper; -import io.cloudevents.v02.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.2 - */ -public final class AttributeMapper { - private AttributeMapper() {} - - static final String HEADER_PREFIX = "ce_"; - - private static final Deserializer DESERIALIZER = - Serdes.String().deserializer(); - - private static final String NULL_ARG = null; - - /** - * Following the signature of {@link BinaryFormatAttributeMapper#map(Map)} - * @param headers Map of Kafka headers - * @return Map with spec attributes and values without parsing - * @see ContextAttributes - */ - public static Map map(Map headers) { - Objects.requireNonNull(headers); - - return headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .filter(header -> header.getKey().startsWith(HEADER_PREFIX)) - .map(header -> new SimpleEntry<>(header.getKey(), - (byte[])header.getValue())) - .map(header -> { - - String key = header.getKey(); - key = key.substring(HEADER_PREFIX.length()); - - String val = DESERIALIZER.deserialize(NULL_ARG, - header.getValue()); - return new SimpleEntry<>(key, val); - }) - .collect(toMap(Entry::getKey, Entry::getValue)); - } - -} diff --git a/kafka/src/main/java/io/cloudevents/v02/kafka/ExtensionMapper.java b/kafka/src/main/java/io/cloudevents/v02/kafka/ExtensionMapper.java deleted file mode 100644 index b9c40173a..000000000 --- a/kafka/src/main/java/io/cloudevents/v02/kafka/ExtensionMapper.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.kafka; - -import java.util.AbstractMap.SimpleEntry; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.apache.kafka.common.serialization.Deserializer; -import org.apache.kafka.common.serialization.Serdes; - -import io.cloudevents.fun.FormatExtensionMapper; -import io.cloudevents.v02.ContextAttributes; - -/** - * - * @author fabiojose - * - */ -public class ExtensionMapper { - private ExtensionMapper() {} - - private static final List RESERVED_HEADERS = - ContextAttributes.VALUES.stream() - .map(attribute -> AttributeMapper - .HEADER_PREFIX + attribute) - .collect(Collectors.toList()); - static { - RESERVED_HEADERS.add("content-type"); - }; - - private static final Deserializer DESERIALIZER = - Serdes.String().deserializer(); - - private static final String NULL_ARG = null; - - /** - * Following the signature of {@link FormatExtensionMapper} - * @param headers The Kafka headers - * @return The potential extensions without parsing - */ - public static Map map(Map headers) { - Objects.requireNonNull(headers); - - // remove all reserved words and the remaining may be extensions - return - headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .filter(header -> { - return !RESERVED_HEADERS.contains(header.getKey()); - }) - .map(header -> new SimpleEntry<>(header.getKey(), - (byte[])header.getValue())) - .map(header -> { - String key = header.getKey(); - String val = DESERIALIZER.deserialize(NULL_ARG, - header.getValue()); - return new SimpleEntry<>(key, val); - }) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } - } diff --git a/kafka/src/main/java/io/cloudevents/v02/kafka/HeaderMapper.java b/kafka/src/main/java/io/cloudevents/v02/kafka/HeaderMapper.java deleted file mode 100644 index 3e14425ea..000000000 --- a/kafka/src/main/java/io/cloudevents/v02/kafka/HeaderMapper.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.kafka; - -import static io.cloudevents.v02.kafka.AttributeMapper.HEADER_PREFIX; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.apache.kafka.common.serialization.Serdes; -import org.apache.kafka.common.serialization.Serializer; - -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.fun.FormatHeaderMapper; -import io.cloudevents.v02.AttributesImpl; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapper { - private HeaderMapper() {} - - private static final Serializer SERIALIZER = - Serdes.String().serializer(); - - /** - * Following the signature of {@link FormatHeaderMapper} - * @param attributes The map of attributes created by - * {@link AttributesImpl#marshal(AttributesImpl)} - * @param extensions The map of extensions created by - * {@link ExtensionFormat#marshal(java.util.Collection)} - * @return The map of Kafka Headers with values as {@code byte[]} - */ - public static Map map(Map attributes, - Map extensions) { - Objects.requireNonNull(attributes); - Objects.requireNonNull(extensions); - - Map result = attributes.entrySet() - .stream() - .filter(attribute -> null!= attribute.getValue()) - .map(attribute -> - new SimpleEntry<>(attribute.getKey() - .toLowerCase(Locale.US), attribute.getValue())) - .map(attribute -> - new SimpleEntry<>(HEADER_PREFIX+attribute.getKey(), - attribute.getValue())) - .map(attribute -> - new SimpleEntry<>(attribute.getKey(), - SERIALIZER.serialize(null, attribute.getValue()))) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - result.putAll( - extensions.entrySet() - .stream() - .filter(extension -> null!= extension.getValue()) - .map(extension -> - new SimpleEntry<>(extension.getKey(), - SERIALIZER.serialize(null, extension.getValue()))) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) - ); - - return result; - } -} diff --git a/kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java b/kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java deleted file mode 100644 index dc175f699..000000000 --- a/kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.kafka; - -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.BinaryMarshaller; -import io.cloudevents.format.StructuredMarshaller; -import io.cloudevents.format.Wire; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.json.Json; -import io.cloudevents.v02.Accessor; -import io.cloudevents.v02.AttributesImpl; -import io.cloudevents.v02.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class Marshallers { - private Marshallers() {} - - private static final Map NO_HEADERS = - new HashMap<>(); - - /** - * Builds a Binary Content Mode marshaller to marshal cloud events as JSON for - * Kafka Transport Binding - * - * @param The 'data' type - * @return A step to provide the {@link CloudEventImpl} and marshal as JSON - * @see BinaryMarshaller - */ - public static EventStep - binary() { - - return - BinaryMarshaller. - builder() - .map(AttributesImpl::marshal) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map) - .map(Json::binaryMarshal) - .builder(Wire::new); - } - - /** - * Builds a Structured Content Mode marshaller to marshal cloud event as JSON for - * Kafka Transport Binding - * @param The 'data' type - * @return A step to provider the {@link CloudEventImpl} and marshal as JSON - * @see StructuredMarshaller - */ - public static EventStep - structured() { - - return - StructuredMarshaller. - builder() - .mime("content-type", "application/cloudevents+json".getBytes()) - .map((event) -> - Json.binaryMarshal(event, NO_HEADERS)) - .skip(); - } -} diff --git a/kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java b/kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java deleted file mode 100644 index 85d18b7e5..000000000 --- a/kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v02.kafka; - -import static io.cloudevents.extensions.DistributedTracingExtension.Format.IN_MEMORY_KEY; -import static io.cloudevents.extensions.DistributedTracingExtension.Format.TRACE_PARENT_KEY; -import static io.cloudevents.extensions.DistributedTracingExtension.Format.TRACE_STATE_KEY; -import static io.cloudevents.extensions.DistributedTracingExtension.Format; -import static java.util.Optional.ofNullable; -import static java.util.AbstractMap.SimpleEntry; - -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.stream.Collectors; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.BinaryUnmarshaller; -import io.cloudevents.format.StructuredUnmarshaller; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.json.Json; -import io.cloudevents.v02.AttributesImpl; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; -import io.cloudevents.v02.kafka.ExtensionMapper; - -/** - * - * @author fabiojose - * - */ -public class Unmarshallers { - private Unmarshallers() {} - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for Kafka Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type) { - - return - BinaryUnmarshaller. - builder() - .map(AttributeMapper::map) - .map(AttributesImpl::unmarshal) - .map("application/json", Json.binaryUmarshaller(type)) - .next() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .builder(CloudEventBuilder.builder()::build); - - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for Kafka Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - @SuppressWarnings("unchecked") - public static HeadersStep - structured(Class typeOfData) { - - return - StructuredUnmarshaller. - builder() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .map((payload, extensions) -> { - - CloudEventImpl event = - Json.> - binaryDecodeValue(payload, CloudEventImpl.class, typeOfData); - - Optional dteFormat = - ofNullable(event.getExtensions().get(IN_MEMORY_KEY)) - .filter(extension -> extension instanceof Map) - .map(extension -> (Map)extension) - .map(extension -> - extension.entrySet() - .stream() - .filter(entry -> - null!= entry.getKey() - && null!= entry.getValue()) - .map(tracing -> - new SimpleEntry<>(tracing.getKey(), - tracing.getValue().toString())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue))) - .map(extension -> { - DistributedTracingExtension dte = - new DistributedTracingExtension(); - dte.setTraceparent(extension.get(TRACE_PARENT_KEY)); - dte.setTracestate(extension.get(TRACE_STATE_KEY)); - - return new Format(dte); - }); - - CloudEventBuilder builder = - CloudEventBuilder.builder(event); - - extensions.get().forEach(extension -> { - builder.withExtension(extension); - }); - - dteFormat.ifPresent(tracing -> { - builder.withExtension(tracing); - }); - - return builder.build(); - }); - } -} diff --git a/kafka/src/main/java/io/cloudevents/v1/kafka/HeaderMapper.java b/kafka/src/main/java/io/cloudevents/v1/kafka/HeaderMapper.java index efcd66acb..f92614ca1 100644 --- a/kafka/src/main/java/io/cloudevents/v1/kafka/HeaderMapper.java +++ b/kafka/src/main/java/io/cloudevents/v1/kafka/HeaderMapper.java @@ -51,7 +51,7 @@ public static Map map(Map attributes, new SimpleEntry<>(attribute.getKey() .toLowerCase(Locale.US), attribute.getValue())) .filter(header -> !header.getKey() - .equals(ContextAttributes.datacontenttype.name())) + .equals(ContextAttributes.DATACONTENTTYPE.name())) .map(attribute -> new SimpleEntry<>(HEADER_PREFIX + attribute.getKey(), attribute.getValue())) @@ -71,7 +71,7 @@ public static Map map(Map attributes, ); Optional.ofNullable(attributes - .get(ContextAttributes.datacontenttype.name())) + .get(ContextAttributes.DATACONTENTTYPE.name())) .ifPresent(dct -> result.put(KAFKA_CONTENT_TYPE, SERIALIZER.serialize(null, dct)) ); diff --git a/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerBinaryTest.java b/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerBinaryTest.java index da9354a59..b0f737598 100644 --- a/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerBinaryTest.java +++ b/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerBinaryTest.java @@ -48,18 +48,18 @@ import io.cloudevents.v02.AttributesImpl; /** - * + * * @author fabiojose * */ public class KafkaConsumerBinaryTest { private static final Duration TIMEOUT = Duration.ofSeconds(30); - + @Test public void should_throws_when_configuration_is_null() { assertThrows(NullPointerException.class, () -> { - new CloudEventsKafkaConsumer(null, + new CloudEventsKafkaConsumer(null, new HeadersStep() { @Override public PayloadStep withHeaders( @@ -69,37 +69,37 @@ public PayloadStep withHeaders( }); }); } - + @Test public void should_be_ok_with_all_required_attributes() throws Exception { // setup Much expected = new Much(); expected.setWow("amz"); - - RecordHeader id = new RecordHeader(HEADER_PREFIX + + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.2".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype}); - + byte[] payload = "{\"wow\" : \"amz\"}".getBytes(); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); @@ -109,19 +109,19 @@ public void should_be_ok_with_all_required_attributes() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + assertFalse(records.isEmpty()); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); @@ -130,34 +130,34 @@ public void should_be_ok_with_all_required_attributes() throws Exception { assertEquals(expected, actual.getData().get()); } } - + @Test public void should_be_ok_with_no_data() throws Exception { - // setup - RecordHeader id = new RecordHeader(HEADER_PREFIX + // setup + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.2".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype}); - + byte[] payload = null; - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); @@ -166,17 +166,17 @@ public void should_be_ok_with_no_data() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); @@ -184,39 +184,39 @@ public void should_be_ok_with_no_data() throws Exception { assertFalse(actual.getData().isPresent()); } } - + @Test public void should_tracing_extension_ok() throws Exception { - // setup - RecordHeader id = new RecordHeader(HEADER_PREFIX + // setup + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.2".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - - RecordHeader traceparent = + + RecordHeader traceparent = new RecordHeader("traceparent", "0".getBytes()); - RecordHeader tracestate = + RecordHeader tracestate = new RecordHeader("tracestate", "congo=4".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype, traceparent, tracestate}); - + byte[] payload = null; - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); @@ -225,17 +225,17 @@ public void should_tracing_extension_ok() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); @@ -244,10 +244,10 @@ public void should_tracing_extension_ok() throws Exception { Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); diff --git a/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerStructuredTest.java b/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerStructuredTest.java index 472a601a4..cebadba75 100644 --- a/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerStructuredTest.java +++ b/kafka/src/test/java/io/cloudevents/v02/kafka/KafkaConsumerStructuredTest.java @@ -45,12 +45,12 @@ import io.cloudevents.v02.AttributesImpl; /** - * + * * @author fabiojose * */ public class KafkaConsumerStructuredTest { - + private static final Duration TIMEOUT = Duration.ofSeconds(8); @Test @@ -58,27 +58,27 @@ public void should_be_ok_with_all_required_attributes() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\"}"; - + Much expected = new Much(); expected.setWow("yes!"); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -88,21 +88,21 @@ public void should_be_ok_with_all_required_attributes() throws Exception { payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getContenttype().get()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected, actual.getData().get()); } @@ -113,25 +113,25 @@ public void should_be_ok_with_no_data() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\"}"; RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -141,49 +141,49 @@ public void should_be_ok_with_no_data() throws Exception { payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getContenttype().get()); - + assertFalse(actual.getData().isPresent()); } } - + @Test public void should_tracing_extension_ok() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\",\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}}"; - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -193,58 +193,58 @@ public void should_tracing_extension_ok() throws Exception { payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getContenttype().get()); assertFalse(actual.getData().isPresent()); - + Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); } } - + @Test public void should_be_with_wrong_value_deserializer() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\",\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}}"; - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -254,28 +254,28 @@ public void should_be_with_wrong_value_deserializer() throws Exception { payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.2", actual.getAttributes().getSpecversion()); + assertEquals("0.2", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getContenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getContenttype().get()); assertFalse(actual.getData().isPresent()); - + Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); diff --git a/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerBinaryTest.java b/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerBinaryTest.java index 758b96527..bf7f0c228 100644 --- a/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerBinaryTest.java +++ b/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerBinaryTest.java @@ -48,18 +48,18 @@ import io.cloudevents.v03.AttributesImpl; /** - * + * * @author fabiojose * */ public class KafkaConsumerBinaryTest { - + private static final Duration TIMEOUT = Duration.ofSeconds(8); @Test public void should_throws_when_configuration_is_null() { assertThrows(NullPointerException.class, () -> { - new CloudEventsKafkaConsumer(null, + new CloudEventsKafkaConsumer(null, new HeadersStep() { @Override public PayloadStep withHeaders( @@ -69,39 +69,39 @@ public PayloadStep withHeaders( }); }); } - + @Test public void should_be_ok_with_all_required_attributes() throws Exception { // setup Much expected = new Much(); expected.setWow("amz"); - - RecordHeader id = new RecordHeader(HEADER_PREFIX + + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.3".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - RecordHeader subject = new RecordHeader(HEADER_PREFIX + RecordHeader subject = new RecordHeader(HEADER_PREFIX + "subject", "subject".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "datacontenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype, subject}); - + byte[] payload = "{\"wow\" : \"amz\"}".getBytes(); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); @@ -110,17 +110,17 @@ public void should_be_ok_with_all_required_attributes() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); @@ -131,36 +131,36 @@ public void should_be_ok_with_all_required_attributes() throws Exception { assertEquals(expected, actual.getData().get()); } } - + @Test public void should_be_ok_with_no_data() throws Exception { - // setup - RecordHeader id = new RecordHeader(HEADER_PREFIX + // setup + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.3".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "datacontenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype}); - + byte[] payload = null; - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -168,17 +168,17 @@ public void should_be_ok_with_no_data() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); @@ -186,41 +186,41 @@ public void should_be_ok_with_no_data() throws Exception { assertFalse(actual.getData().isPresent()); } } - + @Test public void should_tracing_extension_ok() throws Exception { - // setup - RecordHeader id = new RecordHeader(HEADER_PREFIX + // setup + RecordHeader id = new RecordHeader(HEADER_PREFIX + "id", "0x44".getBytes()); - RecordHeader specversion = new RecordHeader(HEADER_PREFIX + RecordHeader specversion = new RecordHeader(HEADER_PREFIX + "specversion", "0.3".getBytes()); - RecordHeader source = new RecordHeader(HEADER_PREFIX + RecordHeader source = new RecordHeader(HEADER_PREFIX + "source", "/source".getBytes()); - RecordHeader type = new RecordHeader(HEADER_PREFIX + RecordHeader type = new RecordHeader(HEADER_PREFIX + "type", "type".getBytes()); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "datacontenttype", "application/json".getBytes()); - - RecordHeader traceparent = + + RecordHeader traceparent = new RecordHeader("traceparent", "0".getBytes()); - RecordHeader tracestate = + RecordHeader tracestate = new RecordHeader("tracestate", "congo=4".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{id, specversion, source, type, contenttype, traceparent, tracestate}); - + byte[] payload = null; - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(binary(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -228,17 +228,17 @@ public void should_tracing_extension_ok() throws Exception { new ConsumerRecord(topic, 0, 0L, 0, TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload, kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + // assert CloudEvent actual = record.value(); assertEquals("0x44", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); @@ -247,10 +247,10 @@ public void should_tracing_extension_ok() throws Exception { Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); diff --git a/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerStructuredTest.java b/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerStructuredTest.java index 4bc278103..2cc3ecb49 100644 --- a/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerStructuredTest.java +++ b/kafka/src/test/java/io/cloudevents/v03/kafka/KafkaConsumerStructuredTest.java @@ -45,7 +45,7 @@ import io.cloudevents.v03.AttributesImpl; /** - * + * * @author fabiojose * */ @@ -58,28 +58,28 @@ public void should_be_ok_with_all_required_attributes() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - + Much expected = new Much(); expected.setWow("yes!"); - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -88,23 +88,23 @@ public void should_be_ok_with_all_required_attributes() throws Exception { TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); assertTrue(actual.getAttributes().getSubject().isPresent()); assertEquals("subject", actual.getAttributes().getSubject().get()); - + assertTrue(actual.getData().isPresent()); assertEquals(expected, actual.getData().get()); } @@ -115,24 +115,24 @@ public void should_be_ok_with_no_data() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\"}"; RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -141,48 +141,48 @@ public void should_be_ok_with_no_data() throws Exception { TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); - + assertFalse(actual.getData().isPresent()); } } - + @Test public void should_tracing_extension_ok() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}}"; - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -191,57 +191,57 @@ public void should_tracing_extension_ok() throws Exception { TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); assertFalse(actual.getData().isPresent()); - + Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); } } - + @Test public void should_be_with_wrong_value_deserializer() throws Exception { // setup Map httpHeaders = new HashMap<>(); httpHeaders.put("Content-Type", "application/cloudevents+json"); - + String payload = "{\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}}"; - + RecordHeader contenttype = new RecordHeader(HEADER_PREFIX + "contenttype", "application/json".getBytes()); - + RecordHeaders kafkaHeaders = new RecordHeaders( new RecordHeader[]{contenttype}); - + final String topic = "binary.c"; - - MockConsumer mockConsumer = + + MockConsumer mockConsumer = new MockConsumer(OffsetResetStrategy.EARLIEST); - + // act - try(CloudEventsKafkaConsumer ceConsumer = + try(CloudEventsKafkaConsumer ceConsumer = new CloudEventsKafkaConsumer<>(structured(Much.class), mockConsumer)){ - + ceConsumer.assign(Collections.singletonList(new TopicPartition(topic, 0))); mockConsumer.seek(new TopicPartition(topic, 0), 0); @@ -250,28 +250,28 @@ public void should_be_with_wrong_value_deserializer() throws Exception { TimestampType.CREATE_TIME, 0L, 0, 0, "0xk", payload.getBytes(), kafkaHeaders) ); - + ConsumerRecords> records = ceConsumer.poll(TIMEOUT); - + ConsumerRecord> record = records.iterator().next(); - + CloudEvent actual = record.value(); assertEquals("x10", actual.getAttributes().getId()); - assertEquals("0.3", actual.getAttributes().getSpecversion()); + assertEquals("0.3", actual.getAttributes().getSpecVersion()); assertEquals(URI.create("/source"), actual.getAttributes().getSource()); assertEquals("event-type", actual.getAttributes().getType()); assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); assertFalse(actual.getData().isPresent()); - + Object tracing = actual.getExtensions() .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - + assertNotNull(tracing); assertTrue(tracing instanceof DistributedTracingExtension); - DistributedTracingExtension dte = + DistributedTracingExtension dte = (DistributedTracingExtension)tracing; assertEquals("0", dte.getTraceparent()); assertEquals("congo=4", dte.getTracestate()); diff --git a/kafka/src/test/java/io/cloudevents/v1/kafka/HeaderMapperTest.java b/kafka/src/test/java/io/cloudevents/v1/kafka/HeaderMapperTest.java index 279226f23..0b8c90fa0 100644 --- a/kafka/src/test/java/io/cloudevents/v1/kafka/HeaderMapperTest.java +++ b/kafka/src/test/java/io/cloudevents/v1/kafka/HeaderMapperTest.java @@ -26,136 +26,135 @@ import org.junit.jupiter.api.Test; import io.cloudevents.v1.ContextAttributes; -import io.cloudevents.v1.kafka.HeaderMapper; /** - * + * * @author fabiojose * */ public class HeaderMapperTest { @Test public void error_when_attributes_map_isnull() { - // setup + // setup Map extensions = new HashMap<>(); - + assertThrows(NullPointerException.class, () -> { // act HeaderMapper.map(null, extensions); }); } - + @Test public void error_when_extensions_map_isnull() { - // setup + // setup Map attributes = new HashMap<>(); - + assertThrows(NullPointerException.class, () -> { // act HeaderMapper.map(attributes, null); }); } - + @Test public void should_not_map_null_attribute_value() { // setup Map attributes = new HashMap<>(); attributes.put("type", null); attributes.put("specversion", "1.0"); - + Map extensions = new HashMap<>(); - + // act Map actual = HeaderMapper.map(attributes, extensions); - + //assert assertFalse(actual.containsKey("ce-type")); } - + @Test public void should_map_datacontenttype_to_content_type() { // setup Map attributes = new HashMap<>(); - attributes.put(ContextAttributes.datacontenttype.name(), "application/json"); - + attributes.put(ContextAttributes.DATACONTENTTYPE.name(), "application/json"); + Map extensions = new HashMap<>(); - + // act Map actual = HeaderMapper.map(attributes, extensions); - + //assert assertTrue(actual.containsKey("content-type")); assertEquals("application/json", new String(actual.get("content-type"))); } - + @Test public void should_not_map_null_extension_value() { // setup Map attributes = new HashMap<>(); attributes.put("type", "mytype"); attributes.put("specversion", "1.0"); - + Map extensions = new HashMap<>(); extensions.put("null-ext", null); extensions.put("comexampleextension1", "value"); - + // act Map actual = HeaderMapper.map(attributes, extensions); - + //assert assertFalse(actual.containsKey("null-ext")); } - + @Test public void should_not_map_absent_contenttype() { // setup Map attributes = new HashMap<>(); attributes.put("type", "mytype"); attributes.put("specversion", "1.0"); - + Map extensions = new HashMap<>(); extensions.put("null-ext", "null-value"); extensions.put("comexampleextension1", "value"); - + // act Map actual = HeaderMapper.map(attributes, extensions); - + //assert - assertFalse(actual.containsKey("Content-Type")); + assertFalse(actual.containsKey("Content-Type")); } - + @Test public void should_map_extension_with_prefix() { // setup Map attributes = new HashMap<>(); attributes.put("type", "mytype"); attributes.put("specversion", "1.0"); - + Map extensions = new HashMap<>(); extensions.put("null-ext", "null-value"); extensions.put("comexampleextension1", "value"); - + // act Map actual = HeaderMapper.map(attributes, extensions); - + //assert assertTrue(actual.containsKey("ce_comexampleextension1")); } - + @Test public void should_all_values_as_byte_array() { // setup Map attributes = new HashMap<>(); attributes.put("type", "mytype"); attributes.put("specversion", "1.0"); - + Map extensions = new HashMap<>(); extensions.put("null-ext", "null-value"); extensions.put("comexampleextension1", "value"); - + // act Map actuals = HeaderMapper.map(attributes, extensions); - + // assert actuals.values() .forEach(actual -> { diff --git a/pom.xml b/pom.xml index 9d066b4d8..7b89d2f0e 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,11 @@ + + slinkydeveloper + Francesco Guardiani + + matzew Matthias Wessendorf @@ -61,6 +66,20 @@ + + api + cdi + http/vertx + kafka + + + + 1.8 + 1.8 + UTF-8 + 2.10.1 + + site @@ -78,19 +97,6 @@ - - api - cdi - http/vertx - kafka - - - - 1.8 - 1.8 - UTF-8 - - release From 0113207a45d04bf246ca90bf6a90569a162bcc66 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Thu, 16 Apr 2020 12:49:17 +0200 Subject: [PATCH 03/21] After writing for months in a toy language, I forgot that modern languages like Java has method overloading Signed-off-by: Francesco Guardiani --- .../main/java/io/cloudevents/CloudEvent.java | 14 +++- api/src/main/java/io/cloudevents/Data.java | 43 ---------- .../common/BaseCloudEventBuilder.java | 49 +++++++++--- .../io/cloudevents/common/BinaryData.java | 58 -------------- .../io/cloudevents/common/CloudEventImpl.java | 79 +++++++++++++++++-- .../java/io/cloudevents/common/JsonData.java | 61 -------------- .../io/cloudevents/common/StringData.java | 58 -------------- 7 files changed, 122 insertions(+), 240 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/Data.java delete mode 100644 api/src/main/java/io/cloudevents/common/BinaryData.java delete mode 100644 api/src/main/java/io/cloudevents/common/JsonData.java delete mode 100644 api/src/main/java/io/cloudevents/common/StringData.java diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 219971894..b02af0ad6 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -15,7 +15,7 @@ */ package io.cloudevents; -import io.cloudevents.v03.CloudEventBuilder; +import com.fasterxml.jackson.databind.JsonNode; import java.util.Map; import java.util.Optional; @@ -35,7 +35,17 @@ public interface CloudEvent { /** * The event data */ - Optional getData(); + Optional getDataAsString() throws DataConversionException; + + /** + * The event data + */ + Optional getDataAsBytes() throws DataConversionException; + + /** + * The event data + */ + Optional getDataAsJson() throws DataConversionException; /** * The event extensions diff --git a/api/src/main/java/io/cloudevents/Data.java b/api/src/main/java/io/cloudevents/Data.java deleted file mode 100644 index 00b5f2c59..000000000 --- a/api/src/main/java/io/cloudevents/Data.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.cloudevents; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import io.cloudevents.common.BinaryData; -import io.cloudevents.common.JsonData; -import io.cloudevents.common.StringData; -import io.cloudevents.json.Json; - -/** - * TODO - * - * @author slinkydeveloper - */ -public interface Data { - - byte[] asBinary() throws DataConversionException; - - String asString() throws DataConversionException; - - JsonNode asJson() throws DataConversionException; - - static Data newJson(JsonNode json) { - return new JsonData(json); - } - - static Data newJson(Object json) { - try { - return Json.MAPPER.valueToTree(json); - } catch (IllegalArgumentException e) { - throw new DataConversionException(json.getClass().toString(), "JsonNode", e); - } - } - - static Data newBinary(byte[] binary) { - return new BinaryData(binary); - } - - static Data newString(String string) { - return new StringData(string); - } - -} diff --git a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java index ea5041fea..22e74953a 100644 --- a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java @@ -1,8 +1,8 @@ package io.cloudevents.common; +import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; -import io.cloudevents.Data; import io.cloudevents.Extension; import java.net.URI; @@ -10,18 +10,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Supplier; public abstract class BaseCloudEventBuilder, T extends Attributes> { // This is a little trick for enabling fluency private B self; - private Data data; + private Object data; private Map extensions; private List materializedExtensions; - @SuppressWarnings("unchecked") public BaseCloudEventBuilder() { this.self = (B)this; @@ -35,17 +33,28 @@ public BaseCloudEventBuilder() { protected abstract T buildAttributes(); - public B withData(String contentType, Data data) { - withDataContentType(contentType); - this.data = data; - return self; + public B withData(String contentType, String data) { + return withData(contentType, (Object) data); } - public B withData(String contentType, URI dataSchema, Data data) { - withDataContentType(contentType); - withDataSchema(dataSchema); - this.data = data; - return self; + public B withData(String contentType, byte[] data) { + return withData(contentType, (Object) data); + } + + public B withData(String contentType, JsonNode data) { + return withData(contentType, (Object) data); + } + + public B withData(String contentType, URI dataSchema, String data) { + return withData(contentType, dataSchema, (Object) data); + } + + public B withData(String contentType, URI dataSchema, byte[] data) { + return withData(contentType, dataSchema, (Object) data); + } + + public B withData(String contentType, URI dataSchema, JsonNode data) { + return withData(contentType, dataSchema, (Object) data); } public B withExtension(String key, Object value) { @@ -61,6 +70,7 @@ public B withExtension(Extension extension) { public CloudEvent build() { CloudEvent event = new CloudEventImpl(this.buildAttributes(), data, extensions); + // Write materialized extensions into the event for (Extension ext : this.materializedExtensions) { ext.writeToEvent(event); } @@ -68,4 +78,17 @@ public CloudEvent build() { return event; } + private B withData(String contentType, Object data) { + withDataContentType(contentType); + this.data = data; + return self; + } + + private B withData(String contentType, URI dataSchema, Object data) { + withDataContentType(contentType); + withDataSchema(dataSchema); + this.data = data; + return self; + } + } diff --git a/api/src/main/java/io/cloudevents/common/BinaryData.java b/api/src/main/java/io/cloudevents/common/BinaryData.java deleted file mode 100644 index 6714588b9..000000000 --- a/api/src/main/java/io/cloudevents/common/BinaryData.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.cloudevents.common; - -import com.fasterxml.jackson.databind.JsonNode; -import io.cloudevents.Data; -import io.cloudevents.DataConversionException; -import io.cloudevents.json.Json; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -public class BinaryData implements Data { - - private byte[] data; - - public BinaryData(byte[] data) { - this.data = data; - } - - @Override - public byte[] asBinary() { - return this.data; - } - - @Override - public String asString() { - return new String(this.data, StandardCharsets.UTF_8); - } - - @Override - public JsonNode asJson() { - try { - return Json.MAPPER.readTree(data); - } catch (IOException e) { - throw new DataConversionException("[]byte", "JsonNode", e); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BinaryData that = (BinaryData) o; - return Arrays.equals(data, that.data); - } - - @Override - public int hashCode() { - return Arrays.hashCode(data); - } - - @Override - public String toString() { - return "BinaryData{" + - "data=" + Arrays.toString(data) + - '}'; - } -} diff --git a/api/src/main/java/io/cloudevents/common/CloudEventImpl.java b/api/src/main/java/io/cloudevents/common/CloudEventImpl.java index 839a6b7f6..a3eb7b51d 100644 --- a/api/src/main/java/io/cloudevents/common/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/common/CloudEventImpl.java @@ -1,9 +1,14 @@ package io.cloudevents.common; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; -import io.cloudevents.Data; +import io.cloudevents.DataConversionException; +import io.cloudevents.json.Json; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -12,10 +17,10 @@ public class CloudEventImpl implements CloudEvent { private final Attributes attributes; - private final Data data; + private final Object data; private final Map extensions; - public CloudEventImpl(Attributes attributes, Data data, Map extensions) { + public CloudEventImpl(Attributes attributes, Object data, Map extensions) { Objects.requireNonNull(attributes); this.attributes = attributes; this.data = data; @@ -28,8 +33,72 @@ public Attributes getAttributes() { } @Override - public Optional getData() { - return Optional.ofNullable(data); + public Optional getDataAsString() { + if (data != null) { + if (data instanceof String) { + return Optional.of((String)data); + } + if (data instanceof byte[]) { + return Optional.of(new String((byte[]) this.data, StandardCharsets.UTF_8)); + } + if (data instanceof JsonNode) { + JsonNode d = (JsonNode) this.data; + try { + return Optional.of(Json.MAPPER.writeValueAsString(data)); + } catch (JsonProcessingException e) { + throw new DataConversionException("JsonNode", "String", e); + } + } + throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); + } + return Optional.empty(); + } + + @Override + public Optional getDataAsBytes() { + if (data != null) { + if (data instanceof String) { + return Optional.of(((String)data).getBytes()); + } + if (data instanceof byte[]) { + return Optional.of((byte[])this.data); + } + if (data instanceof JsonNode) { + JsonNode d = (JsonNode) this.data; + try { + return Optional.of(Json.MAPPER.writeValueAsBytes(data)); + } catch (JsonProcessingException e) { + throw new DataConversionException("JsonNode", "byte[]", e); + } + } + throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); + } + return Optional.empty(); + } + + @Override + public Optional getDataAsJson() { + if (data != null) { + if (data instanceof String) { + try { + return Optional.of(Json.MAPPER.readTree((String)data)); + } catch (IOException e) { + throw new DataConversionException("String", "JsonNode", e); + } + } + if (data instanceof byte[]) { + try { + return Optional.of(Json.MAPPER.readTree((byte[]) data)); + } catch (IOException e) { + throw new DataConversionException("[]byte", "JsonNode", e); + } + } + if (data instanceof JsonNode) { + return Optional.of((JsonNode)this.data); + } + throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); + } + return Optional.empty(); } @Override diff --git a/api/src/main/java/io/cloudevents/common/JsonData.java b/api/src/main/java/io/cloudevents/common/JsonData.java deleted file mode 100644 index 98c32458e..000000000 --- a/api/src/main/java/io/cloudevents/common/JsonData.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.cloudevents.common; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import io.cloudevents.Data; -import io.cloudevents.DataConversionException; -import io.cloudevents.json.Json; - -import java.util.Objects; - -public class JsonData implements Data { - - private JsonNode data; - - public JsonData(JsonNode data) { - this.data = data; - } - - @Override - public byte[] asBinary() { - try { - return Json.MAPPER.writeValueAsBytes(this.data); - } catch (JsonProcessingException e) { - throw new DataConversionException("JsonNode", "[]byte", e); - } - } - - @Override - public String asString() { - try { - return Json.MAPPER.writeValueAsString(data); - } catch (JsonProcessingException e) { - throw new DataConversionException("JsonNode", "String", e); - } - } - - @Override - public JsonNode asJson() { - return this.data; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - JsonData jsonData = (JsonData) o; - return Objects.equals(data, jsonData.data); - } - - @Override - public int hashCode() { - return Objects.hash(data); - } - - @Override - public String toString() { - return "JsonData{" + - "data=" + data + - '}'; - } -} diff --git a/api/src/main/java/io/cloudevents/common/StringData.java b/api/src/main/java/io/cloudevents/common/StringData.java deleted file mode 100644 index f0a7d5b11..000000000 --- a/api/src/main/java/io/cloudevents/common/StringData.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.cloudevents.common; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import io.cloudevents.Data; -import io.cloudevents.DataConversionException; -import io.cloudevents.json.Json; - -import java.io.IOException; -import java.util.Objects; - -public class StringData implements Data { - - private String data; - - public StringData(String data) { - this.data = data; - } - - @Override - public byte[] asBinary() { - return this.data.getBytes(); - } - - @Override - public String asString() { - return this.data; - } - - @Override - public JsonNode asJson() { - try { - return Json.MAPPER.readTree(data); - } catch (IOException e) { - throw new DataConversionException("String", "JsonNode", e); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StringData that = (StringData) o; - return Objects.equals(data, that.data); - } - - @Override - public int hashCode() { - return Objects.hash(data); - } - - @Override - public String toString() { - return "StringData{" + - "data='" + data + '\'' + - '}'; - } -} From ce98e7cbda989738c90f3f673ba478bef90d1769 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Thu, 16 Apr 2020 12:52:50 +0200 Subject: [PATCH 04/21] Extensions Signed-off-by: Francesco Guardiani --- .../io/cloudevents/common/BaseCloudEventBuilder.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java index 22e74953a..d0ce90e80 100644 --- a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java @@ -57,7 +57,17 @@ public B withData(String contentType, URI dataSchema, JsonNode data) { return withData(contentType, dataSchema, (Object) data); } - public B withExtension(String key, Object value) { + public B withExtension(String key, String value) { + this.extensions.put(key, value); + return self; + } + + public B withExtension(String key, Number value) { + this.extensions.put(key, value); + return self; + } + + public B withExtension(String key, boolean value) { this.extensions.put(key, value); return self; } From 59d4a79ed676b0e0409f0d64aee9dd6365d5b57e Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Thu, 16 Apr 2020 16:40:55 +0200 Subject: [PATCH 05/21] Sketching Message APIs Signed-off-by: Francesco Guardiani --- api/pom.xml | 7 +----- .../main/java/io/cloudevents/SpecVersion.java | 23 ++++++++++++++++-- .../DistributedTracingExtension.java | 2 +- .../extensions/ExtensionsParser.java | 10 +++++++- .../BaseCloudEventBuilder.java | 2 +- .../{common => impl}/CloudEventImpl.java | 7 +++++- .../json/CloudEventDeserializer.java | 20 ++++++++++++++++ .../json/CloudEventSerializer.java | 24 +++++++++++++++++++ .../main/java/io/cloudevents/json/Json.java | 16 +++++-------- .../io/cloudevents/message/BinaryMessage.java | 14 +++++++++++ .../message/BinaryMessageVisitor.java | 4 ++++ .../java/io/cloudevents/message/Encoding.java | 7 ++++++ .../java/io/cloudevents/message/Message.java | 7 ++++++ .../message/MessageVisitException.java | 4 ++++ .../message/StructuredMessage.java | 14 +++++++++++ .../message/StructuredMessageVisitor.java | 4 ++++ .../io/cloudevents/v03/CloudEventBuilder.java | 4 ++-- .../io/cloudevents/v1/CloudEventBuilder.java | 3 ++- 18 files changed, 147 insertions(+), 25 deletions(-) rename api/src/main/java/io/cloudevents/{common => impl}/BaseCloudEventBuilder.java (98%) rename api/src/main/java/io/cloudevents/{common => impl}/CloudEventImpl.java (92%) create mode 100644 api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java create mode 100644 api/src/main/java/io/cloudevents/json/CloudEventSerializer.java create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessage.java create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java create mode 100644 api/src/main/java/io/cloudevents/message/Encoding.java create mode 100644 api/src/main/java/io/cloudevents/message/Message.java create mode 100644 api/src/main/java/io/cloudevents/message/MessageVisitException.java create mode 100644 api/src/main/java/io/cloudevents/message/StructuredMessage.java create mode 100644 api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java diff --git a/api/pom.xml b/api/pom.xml index 99993a4ed..739eff9dd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -42,6 +42,7 @@ + com.fasterxml.jackson.core jackson-core @@ -51,12 +52,6 @@ jackson-databind - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - - junit diff --git a/api/src/main/java/io/cloudevents/SpecVersion.java b/api/src/main/java/io/cloudevents/SpecVersion.java index b1d1844c3..841a27566 100644 --- a/api/src/main/java/io/cloudevents/SpecVersion.java +++ b/api/src/main/java/io/cloudevents/SpecVersion.java @@ -1,6 +1,25 @@ package io.cloudevents; public enum SpecVersion { - V03, - V1 + V03("0.3"), + V1("1.0"); + + private final String stringValue; + + SpecVersion(String stringValue) { + this.stringValue = stringValue; + } + + @Override + public String toString() { + return this.stringValue; + } + + public static SpecVersion parse(String sv) { + switch (sv) { + case "0.3": return SpecVersion.V03; + case "1.0": return SpecVersion.V1; + default: throw new IllegalArgumentException("Unrecognized SpecVersion "+ sv); + } + } } diff --git a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java index 5cf520a40..4b09e54e8 100644 --- a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java +++ b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java @@ -3,7 +3,7 @@ import io.cloudevents.CloudEvent; import io.cloudevents.Extension; -public class DistributedTracingExtension implements Extension { +public final class DistributedTracingExtension implements Extension { public static final String TRACEPARENT = "traceparent"; public static final String TRACESTATE = "tracestate"; diff --git a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java index 164f9e575..305716a38 100644 --- a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java +++ b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java @@ -8,9 +8,17 @@ public class ExtensionsParser { + private static class SingletonContainer { + private final static ExtensionsParser INSTANCE = new ExtensionsParser(); + } + + public static ExtensionsParser getInstance() { + return SingletonContainer.INSTANCE; + } + private HashMap, Supplier> extensionFactories; - public ExtensionsParser() { + private ExtensionsParser() { this.extensionFactories = new HashMap<>(); registerExtension(DistributedTracingExtension.class, DistributedTracingExtension::new); } diff --git a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java similarity index 98% rename from api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java rename to api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java index d0ce90e80..45c64d25f 100644 --- a/api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java @@ -1,4 +1,4 @@ -package io.cloudevents.common; +package io.cloudevents.impl; import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.Attributes; diff --git a/api/src/main/java/io/cloudevents/common/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java similarity index 92% rename from api/src/main/java/io/cloudevents/common/CloudEventImpl.java rename to api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index a3eb7b51d..550dbe3cd 100644 --- a/api/src/main/java/io/cloudevents/common/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -1,10 +1,13 @@ -package io.cloudevents.common; +package io.cloudevents.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.DataConversionException; +import io.cloudevents.json.CloudEventSerializer; import io.cloudevents.json.Json; import java.io.IOException; @@ -14,6 +17,8 @@ import java.util.Objects; import java.util.Optional; +@JsonSerialize(using = CloudEventSerializer.class) +@JsonDeserialize(using = CloudEventDeserializer.class) public class CloudEventImpl implements CloudEvent { private final Attributes attributes; diff --git a/api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java b/api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java new file mode 100644 index 000000000..8b4962575 --- /dev/null +++ b/api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java @@ -0,0 +1,20 @@ +package io.cloudevents.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import io.cloudevents.CloudEvent; + +import java.io.IOException; + +public class CloudEventDeserializer extends StdDeserializer { + protected CloudEventDeserializer() { + super(CloudEvent.class); + } + + @Override + public CloudEvent deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return null; + } +} diff --git a/api/src/main/java/io/cloudevents/json/CloudEventSerializer.java b/api/src/main/java/io/cloudevents/json/CloudEventSerializer.java new file mode 100644 index 000000000..a03391e1d --- /dev/null +++ b/api/src/main/java/io/cloudevents/json/CloudEventSerializer.java @@ -0,0 +1,24 @@ +package io.cloudevents.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import io.cloudevents.CloudEvent; + +import java.io.IOException; + +public class CloudEventSerializer extends StdSerializer { + protected CloudEventSerializer() { + super(CloudEvent.class); + } + + @Override + public void serialize(CloudEvent value, JsonGenerator gen, SerializerProvider provider) throws IOException { + JsonSerializer attributesSerializer = provider.findValueSerializer(value.getAttributes().getClass()); + // Serialize attributes + attributesSerializer.serialize(value.getAttributes(), gen, provider); + + + } +} diff --git a/api/src/main/java/io/cloudevents/json/Json.java b/api/src/main/java/io/cloudevents/json/Json.java index c7347c9f2..2e2f0c781 100644 --- a/api/src/main/java/io/cloudevents/json/Json.java +++ b/api/src/main/java/io/cloudevents/json/Json.java @@ -15,31 +15,27 @@ */ package io.cloudevents.json; -import java.io.InputStream; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Map; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; - import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.fun.DataMarshaller; import io.cloudevents.fun.DataUnmarshaller; +import java.io.InputStream; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Map; + public final class Json { public static final ObjectMapper MAPPER = new ObjectMapper(); static { - // add Jackson datatype for ZonedDateTime - MAPPER.registerModule(new Jdk8Module()); - + // add ZonedDateTime ser/de final SimpleModule module = new SimpleModule("Custom ZonedDateTime"); module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer()); diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessage.java b/api/src/main/java/io/cloudevents/message/BinaryMessage.java new file mode 100644 index 000000000..2f793da96 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessage.java @@ -0,0 +1,14 @@ +package io.cloudevents.message; + +@FunctionalInterface +public interface BinaryMessage { + + /** + * + * @param visitor + * @throws MessageVisitException + * @throws IllegalStateException If the message is not a valid binary message + */ + void visit(BinaryMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + +} diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java new file mode 100644 index 000000000..b462b6d33 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java @@ -0,0 +1,4 @@ +package io.cloudevents.message; + +public interface BinaryMessageVisitor { +} diff --git a/api/src/main/java/io/cloudevents/message/Encoding.java b/api/src/main/java/io/cloudevents/message/Encoding.java new file mode 100644 index 000000000..3fb474db1 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/Encoding.java @@ -0,0 +1,7 @@ +package io.cloudevents.message; + +public enum Encoding { + STRUCTURED, + BINARY, + UNKNOWN +} diff --git a/api/src/main/java/io/cloudevents/message/Message.java b/api/src/main/java/io/cloudevents/message/Message.java new file mode 100644 index 000000000..155363d97 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/Message.java @@ -0,0 +1,7 @@ +package io.cloudevents.message; + +public interface Message extends StructuredMessage, BinaryMessage { + + Encoding getEncoding(); + +} diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitException.java b/api/src/main/java/io/cloudevents/message/MessageVisitException.java new file mode 100644 index 000000000..71e21530b --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/MessageVisitException.java @@ -0,0 +1,4 @@ +package io.cloudevents.message; + +public class MessageVisitException extends RuntimeException { +} diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessage.java b/api/src/main/java/io/cloudevents/message/StructuredMessage.java new file mode 100644 index 000000000..f9ae51b96 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/StructuredMessage.java @@ -0,0 +1,14 @@ +package io.cloudevents.message; + +@FunctionalInterface +public interface StructuredMessage { + + /** + * + * @param visitor + * @throws MessageVisitException + * @throws IllegalStateException If the message is not a valid structured message + */ + void visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + +} diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java new file mode 100644 index 000000000..28a2ace4e --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java @@ -0,0 +1,4 @@ +package io.cloudevents.message; + +public interface StructuredMessageVisitor { +} diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java index 8567c4001..f0ce06d22 100644 --- a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java @@ -15,11 +15,11 @@ */ package io.cloudevents.v03; +import io.cloudevents.impl.BaseCloudEventBuilder; + import java.net.URI; import java.time.ZonedDateTime; -import io.cloudevents.common.BaseCloudEventBuilder; - /** * The event builder. diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index 156db0a4c..bde664dab 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -1,8 +1,9 @@ package io.cloudevents.v1; +import io.cloudevents.impl.BaseCloudEventBuilder; + import java.net.URI; import java.time.ZonedDateTime; -import io.cloudevents.common.BaseCloudEventBuilder; /** * From ad5ab5d491ed415fc8dc60264533060977ade900 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Fri, 17 Apr 2020 14:35:11 +0200 Subject: [PATCH 06/21] WIP Signed-off-by: Francesco Guardiani --- api/pom.xml | 6 +- .../main/java/io/cloudevents/CloudEvent.java | 2 +- .../extensions/ExtensionsParser.java | 7 +- .../cloudevents/format/BinaryMarshaller.java | 199 -------------- .../format/BinaryUnmarshaller.java | 243 ------------------ .../io/cloudevents/format/EventFormat.java | 19 ++ .../format/EventFormatProvider.java | 34 +++ .../format/StructuredMarshaller.java | 185 ------------- .../format/StructuredUnmarshaller.java | 159 ------------ .../main/java/io/cloudevents/format/Wire.java | 55 ---- .../cloudevents/format/builder/EventStep.java | 40 --- .../format/builder/HeadersStep.java | 40 --- .../format/builder/MarshalStep.java | 36 --- .../format/builder/PayloadStep.java | 39 --- .../format/builder/UnmarshalStep.java | 36 --- .../json/CloudEventDeserializer.java | 2 +- .../json/CloudEventSerializer.java | 3 +- .../cloudevents/format/json/JsonFormat.java | 74 ++++++ .../json/ZonedDateTimeDeserializer.java | 15 +- .../json/ZonedDateTimeSerializer.java | 14 +- .../impl/BaseCloudEventBuilder.java | 19 +- .../io/cloudevents/impl/CloudEventImpl.java | 3 +- .../main/java/io/cloudevents/json/Json.java | 5 +- .../io/cloudevents/message/BinaryMessage.java | 6 + .../BinaryMessageAttributesVisitor.java | 20 ++ .../BinaryMessageExtensionsVisitor.java | 15 ++ .../message/BinaryMessageVisitor.java | 10 +- .../java/io/cloudevents/message/Message.java | 6 + .../message/MessageVisitException.java | 28 ++ .../message/StructuredMessage.java | 6 + .../message/StructuredMessageVisitor.java | 6 + .../main/java/io/cloudevents/types/Time.java | 7 + .../io/cloudevents/v03/AttributesImpl.java | 187 ++++++-------- .../io/cloudevents/v03/ContextAttributes.java | 2 - .../io/cloudevents/v1/AttributesImpl.java | 160 ++++++------ .../DistributedTracingExtensionTest.java | 60 ++--- .../format/json/JsonFormatTest.java | 22 ++ .../json/CustomEventTypesTest.java | 58 ----- .../json/types/GlusterVolumeClaim.java | 121 --------- .../java/io/cloudevents/json/types/Much.java | 49 ---- .../cloudevents/v1/CloudEventJacksonTest.java | 58 ++--- pom.xml | 2 + 42 files changed, 502 insertions(+), 1556 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/format/BinaryMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java create mode 100644 api/src/main/java/io/cloudevents/format/EventFormat.java create mode 100644 api/src/main/java/io/cloudevents/format/EventFormatProvider.java delete mode 100644 api/src/main/java/io/cloudevents/format/StructuredMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/format/Wire.java delete mode 100644 api/src/main/java/io/cloudevents/format/builder/EventStep.java delete mode 100644 api/src/main/java/io/cloudevents/format/builder/HeadersStep.java delete mode 100644 api/src/main/java/io/cloudevents/format/builder/MarshalStep.java delete mode 100644 api/src/main/java/io/cloudevents/format/builder/PayloadStep.java delete mode 100644 api/src/main/java/io/cloudevents/format/builder/UnmarshalStep.java rename api/src/main/java/io/cloudevents/{ => format}/json/CloudEventDeserializer.java (94%) rename api/src/main/java/io/cloudevents/{ => format}/json/CloudEventSerializer.java (95%) create mode 100644 api/src/main/java/io/cloudevents/format/json/JsonFormat.java rename api/src/main/java/io/cloudevents/{ => format}/json/ZonedDateTimeDeserializer.java (87%) rename api/src/main/java/io/cloudevents/{ => format}/json/ZonedDateTimeSerializer.java (90%) create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessageAttributesVisitor.java create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessageExtensionsVisitor.java create mode 100644 api/src/main/java/io/cloudevents/types/Time.java create mode 100644 api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java delete mode 100644 api/src/test/java/io/cloudevents/json/CustomEventTypesTest.java delete mode 100644 api/src/test/java/io/cloudevents/json/types/GlusterVolumeClaim.java delete mode 100644 api/src/test/java/io/cloudevents/json/types/Much.java diff --git a/api/pom.xml b/api/pom.xml index 739eff9dd..712765d8a 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -54,9 +54,9 @@ - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter + 5.4.2 test diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index b02af0ad6..025ed2d49 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -50,7 +50,7 @@ public interface CloudEvent { /** * The event extensions * - * Extensions values could be String/Number/Boolean/JsonNode + * Extensions values could be String/Number/Boolean */ Map getExtensions(); diff --git a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java index 305716a38..a0d2fa7d8 100644 --- a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java +++ b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java @@ -6,7 +6,7 @@ import java.util.HashMap; import java.util.function.Supplier; -public class ExtensionsParser { +public final class ExtensionsParser { private static class SingletonContainer { private final static ExtensionsParser INSTANCE = new ExtensionsParser(); @@ -27,12 +27,13 @@ public void registerExtension(Class extensionClass, Sup this.extensionFactories.put(extensionClass, factory); } - public Extension parseExtension(Class extensionClass, CloudEvent event) { + @SuppressWarnings("unchecked") + public T parseExtension(Class extensionClass, CloudEvent event) { Supplier factory = extensionFactories.get(extensionClass); if (factory != null) { Extension ext = factory.get(); ext.readFromEvent(event); - return ext; + return (T) ext; } return null; } diff --git a/api/src/main/java/io/cloudevents/format/BinaryMarshaller.java b/api/src/main/java/io/cloudevents/format/BinaryMarshaller.java deleted file mode 100644 index a1b0603bf..000000000 --- a/api/src/main/java/io/cloudevents/format/BinaryMarshaller.java +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import java.util.Collection; -import java.util.Map; -import java.util.function.Supplier; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.format.builder.MarshalStep; -import io.cloudevents.fun.AttributeMarshaller; -import io.cloudevents.fun.DataMarshaller; -import io.cloudevents.fun.ExtensionFormatAccessor; -import io.cloudevents.fun.ExtensionMarshaller; -import io.cloudevents.fun.FormatHeaderMapper; -import io.cloudevents.fun.WireBuilder; - -/** - * - * @author fabiojose - * - */ -public final class BinaryMarshaller { - private BinaryMarshaller() {} - - /** - * Gets a new builder instance - * @param The attributes type - * @param The 'data' type - * @param

The payload type - * @param The type of headers value - * @return - */ - public static - AttributeMarshalStep builder() { - - return new Builder(); - } - - public interface AttributeMarshalStep { - /** - * Marshals the {@link Attributes} instance into a - * {@code Map} - * @param marshaller - * @return - */ - ExtensionsAccessorStep map(AttributeMarshaller marshaller); - } - - public interface ExtensionsAccessorStep { - - /** - * To get access of internal collection of {@link ExtensionFormat} - * @param accessor - * @return - */ - ExtensionsStep map(ExtensionFormatAccessor accessor); - - } - - public interface ExtensionsStep { - /** - * Marshals the collection of {@link ExtensionFormat} into a - * {@code Map} - * @param marshaller - * @return - */ - HeaderMapStep map(ExtensionMarshaller marshaller); - } - - public interface HeaderMapStep { - /** - * Marshals the map of attributes and extensions into a map of headers - * @param mapper - * @return - */ - DataMarshallerStep map(FormatHeaderMapper mapper); - } - - public interface DataMarshallerStep { - /** - * Marshals the 'data' into payload - * @param marshaller - * @return - */ - BuilderStep map(DataMarshaller marshaller); - } - - public interface BuilderStep { - /** - * Builds the {@link Wire} to use for wire transfer - * @param builder - * @return - */ - EventStep builder(WireBuilder builder); - } - - private static final class Builder implements - AttributeMarshalStep, - ExtensionsAccessorStep, - ExtensionsStep, - DataMarshallerStep, - HeaderMapStep, - BuilderStep, - EventStep, - MarshalStep { - - private AttributeMarshaller attributeMarshaller; - private ExtensionFormatAccessor extensionsAccessor; - private ExtensionMarshaller extensionMarshaller; - private FormatHeaderMapper headerMapper; - private DataMarshaller dataMarshaller; - private WireBuilder wireBuilder; - private Supplier> eventSupplier; - - @Override - public ExtensionsAccessorStep map(AttributeMarshaller marshaller) { - this.attributeMarshaller = marshaller; - return this; - } - - @Override - public ExtensionsStep map(ExtensionFormatAccessor accessor) { - this.extensionsAccessor = accessor; - return this; - } - - @Override - public HeaderMapStep map(ExtensionMarshaller marshaller) { - this.extensionMarshaller = marshaller; - return this; - } - - @Override - public DataMarshallerStep map(FormatHeaderMapper mapper) { - this.headerMapper = mapper; - return this; - } - - @Override - public BuilderStep map(DataMarshaller marshaller) { - this.dataMarshaller = marshaller; - return this; - } - - @Override - public EventStep builder(WireBuilder builder) { - this.wireBuilder = builder; - return this; - } - - @Override - public MarshalStep withEvent(Supplier> event) { - this.eventSupplier = event; - return this; - } - - @Override - public Wire marshal() { - CloudEvent event = eventSupplier.get(); - - Map attributesMap = - attributeMarshaller.marshal(event.getAttributes()); - - Collection extensionsFormat = - extensionsAccessor.extensionsOf(event); - - Map extensionsMap = - extensionMarshaller.marshal(extensionsFormat); - - Map headers = - headerMapper.map(attributesMap, extensionsMap); - - P payload = null; - if(event.getData().isPresent()) { - payload = dataMarshaller.marshal(event.getData().get(), - headers); - } - - return wireBuilder.build(payload, headers); - } - } -} diff --git a/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java b/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java deleted file mode 100644 index 5b581ac3d..000000000 --- a/api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.format.builder.PayloadStep; -import io.cloudevents.format.builder.UnmarshalStep; -import io.cloudevents.fun.AttributeUnmarshaller; -import io.cloudevents.fun.BinaryFormatAttributeMapper; -import io.cloudevents.fun.DataUnmarshaller; -import io.cloudevents.fun.EventBuilder; -import io.cloudevents.fun.ExtensionUmarshaller; -import io.cloudevents.fun.FormatExtensionMapper; - -/** - * - * @author fabiojose - * - */ -public final class BinaryUnmarshaller { - private BinaryUnmarshaller() {} - - /** - * Gets a new builder instance - * @param The attributes type - * @param The 'data' type - * @param

The payload type - * @return - */ - public static AttributeMapStep - builder() { - return new Builder(); - } - - public interface AttributeMapStep { - /** - * Maps the map of headers into map of attributes - * @param unmarshaller - * @return - */ - AttributeUmarshallStep map(BinaryFormatAttributeMapper unmarshaller); - } - - public interface AttributeUmarshallStep { - /** - * Unmarshals the map of attributes into instance of {@link Attributes} - * @param unmarshaller - * @return - */ - DataUnmarshallerStep map(AttributeUnmarshaller unmarshaller); - } - - public interface DataUnmarshallerStep { - /** - * Unmarshals the payload into actual 'data' type - * @param unmarshaller - * @return - */ - DataUnmarshallerStep map(String mime, DataUnmarshaller unmarshaller); - - ExtensionsMapStep next(); - } - - public interface ExtensionsMapStep { - /** - * Maps the headers map into map of extensions - * @param mapper - * @return - */ - ExtensionsStep map(FormatExtensionMapper mapper); - } - - public interface ExtensionsStepBegin { - /** - * Starts the configuration for extensions unmarshal - * @return - */ - ExtensionsStep beginExtensions(); - } - - public interface ExtensionsStep { - /** - * Unmarshals a extension, based on the map of extensions. - * - *
- *
- * This is an optional step, because you do not have extensions or - * do not want to process them at all. - * - * @param unmarshaller - * @return - */ - ExtensionsStep map(ExtensionUmarshaller unmarshaller); - - /** - * Ends the configuration for extensions unmarshal - * @return - */ - BuilderStep next(); - } - - public interface BuilderStep
{ - /** - * Takes the builder to build {@link CloudEvent} instances - * @param builder - * @return - */ - HeadersStep builder(EventBuilder builder); - } - - private static final class Builder implements - AttributeMapStep, - AttributeUmarshallStep, - DataUnmarshallerStep, - ExtensionsMapStep, - ExtensionsStep, - BuilderStep, - HeadersStep, - PayloadStep, - UnmarshalStep{ - - private BinaryFormatAttributeMapper attributeMapper; - private AttributeUnmarshaller attributeUnmarshaller; - private Map> dataUnmarshallers = - new HashMap<>(); - private FormatExtensionMapper extensionMapper; - private Set extensionUnmarshallers = - new HashSet<>(); - private EventBuilder eventBuilder; - private Supplier> headersSupplier; - private Supplier

payloadSupplier; - - @Override - public AttributeUmarshallStep map(BinaryFormatAttributeMapper mapper) { - this.attributeMapper = mapper; - return this; - } - - @Override - public DataUnmarshallerStep map(AttributeUnmarshaller unmarshaller) { - this.attributeUnmarshaller = unmarshaller; - return this; - } - - @Override - public DataUnmarshallerStep map(String mime, DataUnmarshaller unmarshaller) { - this.dataUnmarshallers.put(mime, unmarshaller); - return this; - } - - public Builder next() { - return this; - } - - @Override - public ExtensionsStep map(FormatExtensionMapper mapper) { - this.extensionMapper = mapper; - return this; - } - - @Override - public ExtensionsStep map(ExtensionUmarshaller unmarshaller) { - this.extensionUnmarshallers.add(unmarshaller); - return this; - } - - @Override - public HeadersStep builder(EventBuilder builder) { - this.eventBuilder = builder; - return this; - } - - @Override - public PayloadStep withHeaders( - Supplier> headers) { - this.headersSupplier = headers; - return this; - } - - @Override - public UnmarshalStep withPayload(Supplier

payload) { - this.payloadSupplier = payload; - return this; - } - - @Override - public CloudEvent unmarshal() { - - Map headers = headersSupplier.get(); - P payload = payloadSupplier.get(); - - Map attributesMap = attributeMapper.map(headers); - - A attributes = attributeUnmarshaller.unmarshal(attributesMap); - - T data = attributes.getDataContentType() - .map((mime) -> dataUnmarshallers.get(mime)) - .filter(Objects::nonNull) - .map(unmarshaller -> - unmarshaller.unmarshal(payload, attributes)) - .orElse(null); - - final Map extensionsMap = - extensionMapper.map(headers); - - List extensions = - extensionUnmarshallers.stream() - .map(unmarshaller -> - unmarshaller.unmarshal(extensionsMap)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); - - return eventBuilder.build(data, attributes, extensions); - } - } -} diff --git a/api/src/main/java/io/cloudevents/format/EventFormat.java b/api/src/main/java/io/cloudevents/format/EventFormat.java new file mode 100644 index 000000000..b8088eef1 --- /dev/null +++ b/api/src/main/java/io/cloudevents/format/EventFormat.java @@ -0,0 +1,19 @@ +package io.cloudevents.format; + +import io.cloudevents.CloudEvent; + +import java.util.Set; + +public interface EventFormat { + + byte[] serializeToBytes(CloudEvent event); + + String serializeToString(CloudEvent event); + + CloudEvent deserialize(byte[] event); + + CloudEvent deserialize(String event); + + Set supportedContentTypes(); + +} diff --git a/api/src/main/java/io/cloudevents/format/EventFormatProvider.java b/api/src/main/java/io/cloudevents/format/EventFormatProvider.java new file mode 100644 index 000000000..8053d9622 --- /dev/null +++ b/api/src/main/java/io/cloudevents/format/EventFormatProvider.java @@ -0,0 +1,34 @@ +package io.cloudevents.format; + +import io.cloudevents.format.json.JsonFormat; + +import java.util.HashMap; + +public final class EventFormatProvider { + + private static class SingletonContainer { + private final static EventFormatProvider INSTANCE = new EventFormatProvider(); + } + + public static EventFormatProvider getInstance() { + return EventFormatProvider.SingletonContainer.INSTANCE; + } + + private HashMap formats; + + //TODO register stuff with SPI + private EventFormatProvider() { + registerFormat(JsonFormat.getInstance()); + } + + public void registerFormat(EventFormat format) { + for (String k: format.supportedContentTypes()) { + this.formats.put(k, format); + } + } + + public EventFormat resolveFormat(String key) { + return this.formats.get(key); + } + +} diff --git a/api/src/main/java/io/cloudevents/format/StructuredMarshaller.java b/api/src/main/java/io/cloudevents/format/StructuredMarshaller.java deleted file mode 100644 index 009b80037..000000000 --- a/api/src/main/java/io/cloudevents/format/StructuredMarshaller.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Supplier; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.format.builder.MarshalStep; -import io.cloudevents.fun.EnvelopeMarshaller; -import io.cloudevents.fun.ExtensionFormatAccessor; -import io.cloudevents.fun.ExtensionMarshaller; -import io.cloudevents.fun.FormatHeaderMapper; - -/** - * - * @author fabiojose - * - */ -public class StructuredMarshaller { - StructuredMarshaller() {} - - /** - * - * @param The attributes type - * @param The CloudEvents 'data' type - * @param

The CloudEvents marshaled envelope type - * @param The header type - * @return A new builder to build structured mashaller - */ - public static MediaTypeStep - builder() { - return new Builder<>(); - } - - public interface MediaTypeStep { - /** - * Sets the media type of CloudEvents envelope - * @param headerName Example {@code Content-Type} for HTTP - * @param mediaType Example: {@code application/cloudevents+json} - */ - EnvelopeMarshallerStep mime(String headerName, H mediaType); - } - - public interface EnvelopeMarshallerStep { - /** - * Sets the marshaller for the CloudEvent - * @param marshaller - */ - ExtensionAccessorStep map(EnvelopeMarshaller marshaller); - } - - public interface ExtensionAccessorStep { - /** - * To skip the extension special handling - */ - EventStep skip(); - ExtensionMarshallerStep map(ExtensionFormatAccessor accessor); - } - - public interface ExtensionMarshallerStep { - HeaderMapperStep map(ExtensionMarshaller marshaller); - } - - public interface HeaderMapperStep { - EventStep map(FormatHeaderMapper mapper); - } - - private static final class Builder implements - MediaTypeStep, - EnvelopeMarshallerStep, - ExtensionAccessorStep, - ExtensionMarshallerStep, - HeaderMapperStep, - EventStep, - MarshalStep{ - - private static final Map NO_ATTRS = - new HashMap<>(); - - private String headerName; - private H mediaType; - - private EnvelopeMarshaller marshaller; - - private ExtensionFormatAccessor extensionAccessor; - - private ExtensionMarshaller extensionMarshaller; - - private FormatHeaderMapper headerMapper; - - private Supplier> event; - - @Override - public EnvelopeMarshallerStep mime(String headerName, H mediaType) { - Objects.requireNonNull(headerName); - Objects.requireNonNull(mediaType); - - this.headerName = headerName; - this.mediaType = mediaType; - return this; - } - - @Override - public ExtensionAccessorStep map(EnvelopeMarshaller marshaller) { - Objects.requireNonNull(marshaller); - - this.marshaller = marshaller; - return this; - } - - @Override - public EventStep skip() { - return this; - } - - @Override - public ExtensionMarshallerStep map(ExtensionFormatAccessor accessor) { - Objects.requireNonNull(accessor); - - this.extensionAccessor = accessor; - return this; - } - - @Override - public HeaderMapperStep map(ExtensionMarshaller marshaller) { - Objects.requireNonNull(marshaller); - - this.extensionMarshaller = marshaller; - return this; - } - - @Override - public EventStep map(FormatHeaderMapper mapper) { - Objects.requireNonNull(mapper); - - this.headerMapper = mapper; - return this; - } - - @Override - public MarshalStep withEvent(Supplier> event) { - Objects.requireNonNull(event); - - this.event = event; - return this; - } - - @Override - public Wire marshal() { - CloudEvent ce = event.get(); - - P payload = marshaller.marshal(ce); - - Map headers = - Optional.ofNullable(extensionAccessor) - .map(accessor -> accessor.extensionsOf(ce)) - .map(extensions -> extensionMarshaller.marshal(extensions)) - .map(extensions -> headerMapper.map(NO_ATTRS, extensions)) - .orElse(new HashMap<>()); - - headers.put(headerName, mediaType); - - return new Wire<>(payload, headers); - } - } -} diff --git a/api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java b/api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java deleted file mode 100644 index e64a2301a..000000000 --- a/api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.format.builder.PayloadStep; -import io.cloudevents.format.builder.UnmarshalStep; -import io.cloudevents.fun.EnvelopeUnmarshaller; -import io.cloudevents.fun.ExtensionUmarshaller; -import io.cloudevents.fun.FormatExtensionMapper; - -/** - * - * @author fabiojose - * - */ -public class StructuredUnmarshaller { - StructuredUnmarshaller() {} - - public static ExtensionMapperStep - builder() { - return new Builder<>(); - } - - public static interface ExtensionMapperStep { - EnvelopeUnmarshallerStep skip(); - ExtensionUnmarshallerStep map(FormatExtensionMapper mapper); - } - - public static interface ExtensionUnmarshallerStep { - ExtensionUnmarshallerStep map(ExtensionUmarshaller unmarshaller); - EnvelopeUnmarshallerStep next(); - } - - public static interface EnvelopeUnmarshallerStep { - HeadersStep map(EnvelopeUnmarshaller unmarshaller); - } - - private static final class Builder implements - ExtensionMapperStep, - ExtensionUnmarshallerStep, - EnvelopeUnmarshallerStep, - HeadersStep, - PayloadStep, - UnmarshalStep{ - - private FormatExtensionMapper extensionMapper; - - private Set extensionUnmarshallers = - new HashSet<>(); - - private EnvelopeUnmarshaller unmarshaller; - - private Supplier> headersSupplier; - - private Supplier

payloadSupplier; - - @Override - public Builder next() { - return this; - } - - @Override - public EnvelopeUnmarshallerStep skip() { - return this; - } - - @Override - public ExtensionUnmarshallerStep map(FormatExtensionMapper mapper) { - Objects.requireNonNull(mapper); - - this.extensionMapper = mapper; - - return this; - } - - @Override - public ExtensionUnmarshallerStep map(ExtensionUmarshaller unmarshaller) { - Objects.requireNonNull(unmarshaller); - - this.extensionUnmarshallers.add(unmarshaller); - - return this; - } - - @Override - public HeadersStep map(EnvelopeUnmarshaller unmarshaller) { - Objects.requireNonNull(unmarshaller); - - this.unmarshaller = unmarshaller; - - return this; - } - - @Override - public PayloadStep withHeaders(Supplier> headers) { - Objects.requireNonNull(headers); - - this.headersSupplier = headers; - - return this; - } - - @Override - public UnmarshalStep withPayload(Supplier

payload) { - Objects.requireNonNull(payload); - - this.payloadSupplier = payload; - - return this; - } - - @Override - public CloudEvent unmarshal() { - - Map headers = headersSupplier.get(); - P payload = payloadSupplier.get(); - - final Map extensionsMap = - Optional.ofNullable(extensionMapper) - .map(mapper -> mapper.map(headers)) - .orElse(new HashMap<>()); - - return unmarshaller.unmarshal(payload, - () -> - extensionUnmarshallers.stream() - .map(unmarshaller -> - unmarshaller.unmarshal(extensionsMap)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList())); - } - - } -} diff --git a/api/src/main/java/io/cloudevents/format/Wire.java b/api/src/main/java/io/cloudevents/format/Wire.java deleted file mode 100644 index 49b722186..000000000 --- a/api/src/main/java/io/cloudevents/format/Wire.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -/** - * Represents a result of binary marshal, to be used by the wire transfer - * - * @author fabiojose - * @param The payload type - * @param The header key type - * @param The header value type - */ -public class Wire { - - private final T payload; - private final Map headers; - - public Wire(T payload, Map headers) { - Objects.requireNonNull(headers); - this.payload = payload; - this.headers = headers; - } - - /** - * The payload - */ - public Optional getPayload() { - return Optional.ofNullable(payload); - } - - /** - * The headers - */ - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } -} diff --git a/api/src/main/java/io/cloudevents/format/builder/EventStep.java b/api/src/main/java/io/cloudevents/format/builder/EventStep.java deleted file mode 100644 index 8d30da68d..000000000 --- a/api/src/main/java/io/cloudevents/format/builder/EventStep.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format.builder; - -import java.util.function.Supplier; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; - -/** - * - * @author fabiojose - * - * @param The attributes type - * @param The 'data' type - * @param

The payload type - * @param The headers value type - */ -public interface EventStep { - - /** - * Supplies the {@link CloudEvent} instance which will be marshaled - * @param event cloud event to marshal - * @return The next step of builder - */ - MarshalStep withEvent(Supplier> event); -} diff --git a/api/src/main/java/io/cloudevents/format/builder/HeadersStep.java b/api/src/main/java/io/cloudevents/format/builder/HeadersStep.java deleted file mode 100644 index 5502eef81..000000000 --- a/api/src/main/java/io/cloudevents/format/builder/HeadersStep.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format.builder; - -import java.util.Map; -import java.util.function.Supplier; - -import io.cloudevents.Attributes; - -/** - * - * @author fabiojose - * - * @param The attributes type - * @param The 'data' type - * @param

The payload type - */ -public interface HeadersStep { - - /** - * Supplies a map of headers to be used by the unmarshaller - * @param headers - * @return The next step of builder - */ - PayloadStep withHeaders(Supplier> headers); - -} diff --git a/api/src/main/java/io/cloudevents/format/builder/MarshalStep.java b/api/src/main/java/io/cloudevents/format/builder/MarshalStep.java deleted file mode 100644 index 408320503..000000000 --- a/api/src/main/java/io/cloudevents/format/builder/MarshalStep.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format.builder; - -import io.cloudevents.CloudEvent; -import io.cloudevents.format.Wire; - -/** - * - * @author fabiojose - * - * @param

The payload type - * @param The headers value type - */ -public interface MarshalStep { - - /** - * Marshals the {@link CloudEvent} instance as {@link Wire} - * @return The wire to use in the transports bindings - */ - Wire marshal(); - -} diff --git a/api/src/main/java/io/cloudevents/format/builder/PayloadStep.java b/api/src/main/java/io/cloudevents/format/builder/PayloadStep.java deleted file mode 100644 index 854888fdf..000000000 --- a/api/src/main/java/io/cloudevents/format/builder/PayloadStep.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format.builder; - -import java.util.function.Supplier; - -import io.cloudevents.Attributes; - -/** - * - * @author fabiojose - * - * @param The attributes type - * @param The 'data' type - * @param

The payload type - */ -public interface PayloadStep { - - /** - * Supplies the payload that will be used by the unmarshaller - * @param payload - * @return The next step o builder - */ - UnmarshalStep withPayload(Supplier

payload); - -} diff --git a/api/src/main/java/io/cloudevents/format/builder/UnmarshalStep.java b/api/src/main/java/io/cloudevents/format/builder/UnmarshalStep.java deleted file mode 100644 index 6c730c678..000000000 --- a/api/src/main/java/io/cloudevents/format/builder/UnmarshalStep.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format.builder; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; - -/** - * - * @author fabiojose - * - * @param The attributes type - * @param The 'data' type - */ -public interface UnmarshalStep { - - /** - * Unmarshals the payload and headers to {@link CloudEvent} instance - * @return New cloud event instance - */ - CloudEvent unmarshal(); - -} diff --git a/api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java b/api/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java similarity index 94% rename from api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java rename to api/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java index 8b4962575..a05928bfa 100644 --- a/api/src/main/java/io/cloudevents/json/CloudEventDeserializer.java +++ b/api/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java @@ -1,4 +1,4 @@ -package io.cloudevents.json; +package io.cloudevents.format.json; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/api/src/main/java/io/cloudevents/json/CloudEventSerializer.java b/api/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java similarity index 95% rename from api/src/main/java/io/cloudevents/json/CloudEventSerializer.java rename to api/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java index a03391e1d..40a54ec98 100644 --- a/api/src/main/java/io/cloudevents/json/CloudEventSerializer.java +++ b/api/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java @@ -1,4 +1,4 @@ -package io.cloudevents.json; +package io.cloudevents.format.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; @@ -19,6 +19,5 @@ public void serialize(CloudEvent value, JsonGenerator gen, SerializerProvider pr // Serialize attributes attributesSerializer.serialize(value.getAttributes(), gen, provider); - } } diff --git a/api/src/main/java/io/cloudevents/format/json/JsonFormat.java b/api/src/main/java/io/cloudevents/format/json/JsonFormat.java new file mode 100644 index 000000000..79ddc0b43 --- /dev/null +++ b/api/src/main/java/io/cloudevents/format/json/JsonFormat.java @@ -0,0 +1,74 @@ +/** + * Copyright 2018 The CloudEvents Authors + * + * Licensed 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 io.cloudevents.format.json; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import io.cloudevents.CloudEvent; +import io.cloudevents.format.EventFormat; + +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.Set; + +public final class JsonFormat implements EventFormat { + + public static final ObjectMapper MAPPER = new ObjectMapper(); + + static { + // add ZonedDateTime ser/de + final SimpleModule module = new SimpleModule("Custom ZonedDateTime"); + module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); + module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer()); + MAPPER.registerModule(module); + } + + + private static class SingletonContainer { + private final static JsonFormat INSTANCE = new JsonFormat(); + } + + public static JsonFormat getInstance() { + return JsonFormat.SingletonContainer.INSTANCE; + } + + private JsonFormat() {} + + @Override + public byte[] serializeToBytes(CloudEvent event) { + return new byte[0]; + } + + @Override + public String serializeToString(CloudEvent event) { + return null; + } + + @Override + public CloudEvent deserialize(byte[] event) { + return null; + } + + @Override + public CloudEvent deserialize(String event) { + return null; + } + + @Override + public Set supportedContentTypes() { + return Collections.singleton("application/cloudevents+json"); + } +} diff --git a/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java b/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java similarity index 87% rename from api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java rename to api/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java index 26aa4ef2c..b6e18244c 100644 --- a/api/src/main/java/io/cloudevents/json/ZonedDateTimeDeserializer.java +++ b/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudevents.json; - -import java.io.IOException; -import java.time.DateTimeException; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; +package io.cloudevents.format.json; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import io.cloudevents.types.Time; + +import java.io.IOException; +import java.time.DateTimeException; +import java.time.ZonedDateTime; public class ZonedDateTimeDeserializer extends StdDeserializer { private static final long serialVersionUID = 1L; @@ -37,9 +37,8 @@ protected ZonedDateTimeDeserializer(Class vc) { @Override public ZonedDateTime deserialize(JsonParser jsonparser, DeserializationContext ctxt) throws IOException { - // not serializing timezone data yet try { - return ZonedDateTime.parse(jsonparser.getText(), Json.RFC3339_DATE_FORMAT); + return ZonedDateTime.parse(jsonparser.getText(), Time.RFC3339_DATE_FORMAT); } catch (DateTimeException e) { throw new IllegalArgumentException("could not parse"); } diff --git a/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java b/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java similarity index 90% rename from api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java rename to api/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java index 21d154627..aeca80736 100644 --- a/api/src/main/java/io/cloudevents/json/ZonedDateTimeSerializer.java +++ b/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.cloudevents.json; - -import java.io.IOException; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; +package io.cloudevents.format.json; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import io.cloudevents.types.Time; + +import java.io.IOException; +import java.time.ZonedDateTime; public class ZonedDateTimeSerializer extends StdSerializer { @@ -38,9 +38,7 @@ protected ZonedDateTimeSerializer(Class t, boolean dummy) { @Override public void serialize(ZonedDateTime time, JsonGenerator generator, SerializerProvider provider) throws IOException { - - generator.writeString(time.format(Json.RFC3339_DATE_FORMAT)); - + generator.writeString(time.format(Time.RFC3339_DATE_FORMAT)); } } diff --git a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java index 45c64d25f..105c59a09 100644 --- a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java @@ -33,28 +33,31 @@ public BaseCloudEventBuilder() { protected abstract T buildAttributes(); + //TODO builder should accept data as Object and use data codecs (that we need to implement) + // to encode data + public B withData(String contentType, String data) { - return withData(contentType, (Object) data); + return withEncodedData(contentType, (Object) data); } public B withData(String contentType, byte[] data) { - return withData(contentType, (Object) data); + return withEncodedData(contentType, (Object) data); } public B withData(String contentType, JsonNode data) { - return withData(contentType, (Object) data); + return withEncodedData(contentType, (Object) data); } public B withData(String contentType, URI dataSchema, String data) { - return withData(contentType, dataSchema, (Object) data); + return withEncodedata(contentType, dataSchema, (Object) data); } public B withData(String contentType, URI dataSchema, byte[] data) { - return withData(contentType, dataSchema, (Object) data); + return withEncodedata(contentType, dataSchema, (Object) data); } public B withData(String contentType, URI dataSchema, JsonNode data) { - return withData(contentType, dataSchema, (Object) data); + return withEncodedata(contentType, dataSchema, (Object) data); } public B withExtension(String key, String value) { @@ -88,13 +91,13 @@ public CloudEvent build() { return event; } - private B withData(String contentType, Object data) { + private B withEncodedData(String contentType, Object data) { withDataContentType(contentType); this.data = data; return self; } - private B withData(String contentType, URI dataSchema, Object data) { + private B withEncodedata(String contentType, URI dataSchema, Object data) { withDataContentType(contentType); withDataSchema(dataSchema); this.data = data; diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index 550dbe3cd..c443591dd 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -7,7 +7,8 @@ import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.DataConversionException; -import io.cloudevents.json.CloudEventSerializer; +import io.cloudevents.format.json.CloudEventDeserializer; +import io.cloudevents.format.json.CloudEventSerializer; import io.cloudevents.json.Json; import java.io.IOException; diff --git a/api/src/main/java/io/cloudevents/json/Json.java b/api/src/main/java/io/cloudevents/json/Json.java index 2e2f0c781..48fdc66a2 100644 --- a/api/src/main/java/io/cloudevents/json/Json.java +++ b/api/src/main/java/io/cloudevents/json/Json.java @@ -22,12 +22,13 @@ import com.fasterxml.jackson.databind.type.TypeFactory; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; +import io.cloudevents.format.json.ZonedDateTimeDeserializer; +import io.cloudevents.format.json.ZonedDateTimeSerializer; import io.cloudevents.fun.DataMarshaller; import io.cloudevents.fun.DataUnmarshaller; import java.io.InputStream; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.util.Map; public final class Json { @@ -42,8 +43,6 @@ public final class Json { MAPPER.registerModule(module); } - protected static final DateTimeFormatter RFC3339_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); - public static String encode(final CloudEvent event) throws IllegalStateException { } diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessage.java b/api/src/main/java/io/cloudevents/message/BinaryMessage.java index 2f793da96..28b4390a4 100644 --- a/api/src/main/java/io/cloudevents/message/BinaryMessage.java +++ b/api/src/main/java/io/cloudevents/message/BinaryMessage.java @@ -1,5 +1,7 @@ package io.cloudevents.message; +import io.cloudevents.CloudEvent; + @FunctionalInterface public interface BinaryMessage { @@ -11,4 +13,8 @@ public interface BinaryMessage { */ void visit(BinaryMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { + //TODO + }; + } diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageAttributesVisitor.java b/api/src/main/java/io/cloudevents/message/BinaryMessageAttributesVisitor.java new file mode 100644 index 000000000..1cb73baf2 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageAttributesVisitor.java @@ -0,0 +1,20 @@ +package io.cloudevents.message; + +import io.cloudevents.types.Time; + +import java.net.URI; +import java.time.ZonedDateTime; + +public interface BinaryMessageAttributesVisitor { + + void setAttribute(String name, String value) throws MessageVisitException; + + default void setAttribute(String name, URI value) throws MessageVisitException { + setAttribute(name, value.toString()); + } + + default void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { + setAttribute(name, value.format(Time.RFC3339_DATE_FORMAT)); + } + +} diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageExtensionsVisitor.java b/api/src/main/java/io/cloudevents/message/BinaryMessageExtensionsVisitor.java new file mode 100644 index 000000000..1fa2948f7 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageExtensionsVisitor.java @@ -0,0 +1,15 @@ +package io.cloudevents.message; + +public interface BinaryMessageExtensionsVisitor { + + void setExtension(String name, String value) throws MessageVisitException; + + default void setExtension(String name, Number value) throws MessageVisitException { + setExtension(name, value.toString()); + } + + default void setExtension(String name, Boolean value) throws MessageVisitException { + setExtension(name, value.toString()); + } + +} diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java index b462b6d33..b4e236820 100644 --- a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java @@ -1,4 +1,12 @@ package io.cloudevents.message; -public interface BinaryMessageVisitor { +import io.cloudevents.SpecVersion; + +public interface BinaryMessageVisitor extends BinaryMessageAttributesVisitor, BinaryMessageExtensionsVisitor { + + void setSpecVersion(SpecVersion specVersion) throws MessageVisitException; + + // TODO one day we'll convert this to some byte stream + void setBody(byte[] value) throws MessageVisitException; + } diff --git a/api/src/main/java/io/cloudevents/message/Message.java b/api/src/main/java/io/cloudevents/message/Message.java index 155363d97..38adf7013 100644 --- a/api/src/main/java/io/cloudevents/message/Message.java +++ b/api/src/main/java/io/cloudevents/message/Message.java @@ -1,7 +1,13 @@ package io.cloudevents.message; +import io.cloudevents.CloudEvent; + public interface Message extends StructuredMessage, BinaryMessage { Encoding getEncoding(); + default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { + //TODO + }; + } diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitException.java b/api/src/main/java/io/cloudevents/message/MessageVisitException.java index 71e21530b..e01bcf142 100644 --- a/api/src/main/java/io/cloudevents/message/MessageVisitException.java +++ b/api/src/main/java/io/cloudevents/message/MessageVisitException.java @@ -1,4 +1,32 @@ package io.cloudevents.message; public class MessageVisitException extends RuntimeException { + + public enum MessageVisitExceptionKind { + INVALID_ATTRIBUTE_TYPE + } + + private MessageVisitExceptionKind kind; + + public MessageVisitException(MessageVisitExceptionKind kind, String message) { + super(message); + this.kind = kind; + } + + public MessageVisitException(MessageVisitExceptionKind kind, String message, Throwable cause) { + super(message, cause); + this.kind = kind; + } + + public MessageVisitExceptionKind getKind() { + return kind; + } + + public static MessageVisitException newInvalidAttributeType(String attributeName, Class clazz) { + return new MessageVisitException( + MessageVisitExceptionKind.INVALID_ATTRIBUTE_TYPE, + "Invalid attribute type for " + attributeName + ": " + clazz.getCanonicalName() + ); + } + } diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessage.java b/api/src/main/java/io/cloudevents/message/StructuredMessage.java index f9ae51b96..cc579e513 100644 --- a/api/src/main/java/io/cloudevents/message/StructuredMessage.java +++ b/api/src/main/java/io/cloudevents/message/StructuredMessage.java @@ -1,5 +1,7 @@ package io.cloudevents.message; +import io.cloudevents.CloudEvent; + @FunctionalInterface public interface StructuredMessage { @@ -11,4 +13,8 @@ public interface StructuredMessage { */ void visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { + //TODO + }; + } diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java index 28a2ace4e..ffff5c322 100644 --- a/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java +++ b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java @@ -1,4 +1,10 @@ package io.cloudevents.message; +import io.cloudevents.format.EventFormat; + public interface StructuredMessageVisitor { + + // TODO one day we'll convert this to some byte stream + void setEvent(EventFormat format, byte[] value) throws MessageVisitException; + } diff --git a/api/src/main/java/io/cloudevents/types/Time.java b/api/src/main/java/io/cloudevents/types/Time.java new file mode 100644 index 000000000..e00f965af --- /dev/null +++ b/api/src/main/java/io/cloudevents/types/Time.java @@ -0,0 +1,7 @@ +package io.cloudevents.types; + +import java.time.format.DateTimeFormatter; + +public final class Time { + public static final DateTimeFormatter RFC3339_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); +} diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index 4aa927346..7697a8f6d 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -15,28 +15,13 @@ */ package io.cloudevents.v03; -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; +import io.cloudevents.Attributes; +import io.cloudevents.SpecVersion; import java.net.URI; -import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.Optional; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import io.cloudevents.Attributes; -import io.cloudevents.SpecVersion; - /** * The event attributes implementation for v0.3 * @@ -93,13 +78,9 @@ public Optional getDataSchema() { public Optional getSchemaUrl() { return Optional.ofNullable(schemaurl); } - /** - * {@inheritDoc} - */ public Optional getDataContentType() { return Optional.ofNullable(datacontenttype); } - public Optional getSubject() { return Optional.ofNullable(subject); } @@ -112,87 +93,85 @@ public String toString() { + ", datacontenttype=" + datacontenttype + ", subject=" + subject + "]"; } - - /** - * Used by the Jackson framework to unmarshall. - */ - @JsonCreator - public static AttributesImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("specversion") String specversion, - @JsonProperty("type") String type, - @JsonProperty("time") ZonedDateTime time, - @JsonProperty("schemaurl") URI schemaurl, - @JsonProperty("datacontentenconding") String datacontentencoding, - @JsonProperty("datacontenttype") String datacontenttype, - @JsonProperty("subject") String subject) { - - return new AttributesImpl(id, source, specversion, type, time, - schemaurl, datacontentencoding, datacontenttype, subject); - } - - /** - * Creates the marshaller instance to marshall {@link AttributesImpl} as - * a {@link Map} of strings - */ - public static Map marshal(AttributesImpl attributes) { - Objects.requireNonNull(attributes); - - Map result = new HashMap<>(); - - result.put(ContextAttributes.TYPE.name(), - attributes.getType()); - result.put(ContextAttributes.SPECVERSION.name(), - attributes.getSpecVersion()); - result.put(ContextAttributes.SOURCE.name(), - attributes.getSource().toString()); - result.put(ContextAttributes.ID.name(), - attributes.getId()); - - attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.TIME.name(), - value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); - attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.SCHEMAURL.name(), - schema.toString())); - attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.DATACONTENTTYPE.name(), ct)); - attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.DATACONTENTENCODING.name(), dce)); - attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); - - return result; - } - - /** - * The attribute unmarshaller for the binary format, that receives a - * {@code Map} with attributes names as String and value as String. - */ - public static AttributesImpl unmarshal(Map attributes) { - String type = attributes.get(ContextAttributes.TYPE.name()); - ZonedDateTime time = - Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) - .map((t) -> ZonedDateTime.parse(t, - ISO_ZONED_DATE_TIME)) - .orElse(null); - - String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); - URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); - - URI schemaurl = - Optional.ofNullable(attributes.get(ContextAttributes.SCHEMAURL.name())) - .map(URI::create) - .orElse(null); - - String id = attributes.get(ContextAttributes.ID.name()); - - String datacontenttype = - attributes.get(ContextAttributes.DATACONTENTTYPE.name()); - - String datacontentencoding = - attributes.get(ContextAttributes.DATACONTENTENCODING.name()); - - String subject = attributes.get(ContextAttributes.SUBJECT.name()); - - return AttributesImpl.build(id, source, specversion, type, - time, schemaurl, datacontentencoding, - datacontenttype, subject); - } +// +// /** +// * Used by the Jackson framework to unmarshall. +// */ +// @JsonCreator +// public static AttributesImpl build( +// @JsonProperty("id") String id, +// @JsonProperty("source") URI source, +// @JsonProperty("type") String type, +// @JsonProperty("time") ZonedDateTime time, +// @JsonProperty("schemaurl") URI schemaurl, +// @JsonProperty("datacontenttype") String datacontenttype, +// @JsonProperty("subject") String subject) { +// +// return new AttributesImpl(id, source, type, time, +// schemaurl, datacontenttype, subject); +// } +// +// /** +// * Creates the marshaller instance to marshall {@link AttributesImpl} as +// * a {@link Map} of strings +// */ +// public static Map marshal(AttributesImpl attributes) { +// Objects.requireNonNull(attributes); +// +// Map result = new HashMap<>(); +// +// result.put(ContextAttributes.TYPE.name(), +// attributes.getType()); +// result.put(ContextAttributes.SPECVERSION.name(), +// attributes.getSpecVersion()); +// result.put(ContextAttributes.SOURCE.name(), +// attributes.getSource().toString()); +// result.put(ContextAttributes.ID.name(), +// attributes.getId()); +// +// attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.TIME.name(), +// value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); +// attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.SCHEMAURL.name(), +// schema.toString())); +// attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.DATACONTENTTYPE.name(), ct)); +// attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.DATACONTENTENCODING.name(), dce)); +// attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); +// +// return result; +// } +// +// /** +// * The attribute unmarshaller for the binary format, that receives a +// * {@code Map} with attributes names as String and value as String. +// */ +// public static AttributesImpl unmarshal(Map attributes) { +// String type = attributes.get(ContextAttributes.TYPE.name()); +// ZonedDateTime time = +// Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) +// .map((t) -> ZonedDateTime.parse(t, +// ISO_ZONED_DATE_TIME)) +// .orElse(null); +// +// String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); +// URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); +// +// URI schemaurl = +// Optional.ofNullable(attributes.get(ContextAttributes.SCHEMAURL.name())) +// .map(URI::create) +// .orElse(null); +// +// String id = attributes.get(ContextAttributes.ID.name()); +// +// String datacontenttype = +// attributes.get(ContextAttributes.DATACONTENTTYPE.name()); +// +// String datacontentencoding = +// attributes.get(ContextAttributes.DATACONTENTENCODING.name()); +// +// String subject = attributes.get(ContextAttributes.SUBJECT.name()); +// +// return AttributesImpl.build(id, source, specversion, type, +// time, schemaurl, datacontentencoding, +// datacontenttype, subject); +// } } diff --git a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java index 1b6f686c6..3c35ee2f9 100644 --- a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java @@ -26,7 +26,6 @@ * */ public enum ContextAttributes { - ID, SOURCE, SPECVERSION, @@ -36,7 +35,6 @@ public enum ContextAttributes { DATACONTENTTYPE, DATACONTENTENCODING, SUBJECT; - public static final List VALUES = Arrays.stream(ContextAttributes.values()) .map(Enum::name) diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 2e603f469..6a4f492d6 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -15,25 +15,13 @@ */ package io.cloudevents.v1; -import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME; +import io.cloudevents.Attributes; +import io.cloudevents.SpecVersion; import java.net.URI; -import java.time.ZoneOffset; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.Optional; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import io.cloudevents.Attributes; -import io.cloudevents.SpecVersion; -import io.cloudevents.json.ZonedDateTimeDeserializer; - /** * * @author fabiojose @@ -112,76 +100,76 @@ public String toString() { + ", time=" + time + "]"; } - /** - * Used by the Jackson framework to unmarshall. - */ - @JsonCreator - public static AttributesImpl build( - @JsonProperty("id") String id, - @JsonProperty("source") URI source, - @JsonProperty("type") String type, - @JsonProperty("datacontenttype") String datacontenttype, - @JsonProperty("dataschema") URI dataschema, - @JsonProperty("subject") String subject, - @JsonProperty("time") ZonedDateTime time) { - - return new AttributesImpl(id, source, type, - datacontenttype, dataschema, subject, time); - } - - /** - * Creates the marshaller instance to marshall {@link AttributesImpl} as - * a {@link Map} of strings - */ - public static Map marshal(AttributesImpl attributes) { - Objects.requireNonNull(attributes); - Map result = new HashMap<>(); - - result.put(ContextAttributes.ID.name(), - attributes.getId()); - result.put(ContextAttributes.SOURCE.name(), - attributes.getSource().toString()); - result.put(ContextAttributes.TYPE.name(), - attributes.getType()); - - attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.DATACONTENTTYPE.name(), dct)); - attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.DATASCHEMA.name(), - dataschema.toString())); - attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); - attributes.getTime().ifPresent(time -> result.put(ContextAttributes.TIME.name(), - time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); - - return result; - } - - /** - * The attribute unmarshaller for the binary format, that receives a - * {@code Map} with attributes names as String and value as String. - */ - public static AttributesImpl unmarshal(Map attributes) { - String type = attributes.get(ContextAttributes.TYPE.name()); - ZonedDateTime time = - Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) - .map((t) -> ZonedDateTime.parse(t, - ISO_ZONED_DATE_TIME)) - .orElse(null); - - String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); - URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); - - URI dataschema = - Optional.ofNullable(attributes.get(ContextAttributes.DATASCHEMA.name())) - .map(URI::create) - .orElse(null); - - String id = attributes.get(ContextAttributes.ID.name()); - - String datacontenttype = - attributes.get(ContextAttributes.DATACONTENTTYPE.name()); - - String subject = attributes.get(ContextAttributes.SUBJECT.name()); - - return AttributesImpl.build(id, source, type, - datacontenttype, dataschema, subject, time); - } +// /** +// * Used by the Jackson framework to unmarshall. +// */ +// @JsonCreator +// public static AttributesImpl build( +// @JsonProperty("id") String id, +// @JsonProperty("source") URI source, +// @JsonProperty("type") String type, +// @JsonProperty("datacontenttype") String datacontenttype, +// @JsonProperty("dataschema") URI dataschema, +// @JsonProperty("subject") String subject, +// @JsonProperty("time") ZonedDateTime time) { +// +// return new AttributesImpl(id, source, type, +// datacontenttype, dataschema, subject, time); +// } +// +// /** +// * Creates the marshaller instance to marshall {@link AttributesImpl} as +// * a {@link Map} of strings +// */ +// public static Map marshal(AttributesImpl attributes) { +// Objects.requireNonNull(attributes); +// Map result = new HashMap<>(); +// +// result.put(ContextAttributes.ID.name(), +// attributes.getId()); +// result.put(ContextAttributes.SOURCE.name(), +// attributes.getSource().toString()); +// result.put(ContextAttributes.TYPE.name(), +// attributes.getType()); +// +// attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.DATACONTENTTYPE.name(), dct)); +// attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.DATASCHEMA.name(), +// dataschema.toString())); +// attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); +// attributes.getTime().ifPresent(time -> result.put(ContextAttributes.TIME.name(), +// time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); +// +// return result; +// } +// +// /** +// * The attribute unmarshaller for the binary format, that receives a +// * {@code Map} with attributes names as String and value as String. +// */ +// public static AttributesImpl unmarshal(Map attributes) { +// String type = attributes.get(ContextAttributes.TYPE.name()); +// ZonedDateTime time = +// Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) +// .map((t) -> ZonedDateTime.parse(t, +// ISO_ZONED_DATE_TIME)) +// .orElse(null); +// +// String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); +// URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); +// +// URI dataschema = +// Optional.ofNullable(attributes.get(ContextAttributes.DATASCHEMA.name())) +// .map(URI::create) +// .orElse(null); +// +// String id = attributes.get(ContextAttributes.ID.name()); +// +// String datacontenttype = +// attributes.get(ContextAttributes.DATACONTENTTYPE.name()); +// +// String subject = attributes.get(ContextAttributes.SUBJECT.name()); +// +// return AttributesImpl.build(id, source, type, +// datacontenttype, dataschema, subject, time); +// } } diff --git a/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java b/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java index b7b689889..e58cbc7db 100644 --- a/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java +++ b/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java @@ -15,52 +15,46 @@ */ package io.cloudevents.extensions; -import static org.junit.Assert.assertEquals; +import io.cloudevents.CloudEvent; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; /** - * + * * @author fabiojose * */ public class DistributedTracingExtensionTest { @Test - public void should_transport_format_ok() { - // setup + public void writeExtension() { DistributedTracingExtension tracing = new DistributedTracingExtension(); tracing.setTraceparent("parent"); tracing.setTracestate("state"); - - // act - ExtensionFormat format = - new DistributedTracingExtension.Format(tracing); - - // assert - assertEquals("parent", format.transport().get("traceparent")); - assertEquals("state", format.transport().get("tracestate")); + + CloudEvent event = CloudEvent.build().build(); + tracing.writeToEvent(event); + + assertThat(event.getExtensions()) + .containsEntry(DistributedTracingExtension.TRACEPARENT, "parent") + .containsEntry(DistributedTracingExtension.TRACESTATE, "state"); } - + @Test - public void should_inmemory_format_ok() { - // setup - DistributedTracingExtension tracing = new DistributedTracingExtension(); - tracing.setTraceparent("parent"); - tracing.setTracestate("state"); - - // act - ExtensionFormat format = - new DistributedTracingExtension.Format(tracing); - - // assert - assertEquals("distributedTracing", format.memory().getKey()); - assertEquals(DistributedTracingExtension.class, format.memory().getValueType()); - - assertEquals("parent", - ((DistributedTracingExtension)format.memory().getValue()).getTraceparent()); - - assertEquals("state", - ((DistributedTracingExtension)format.memory().getValue()).getTracestate()); + public void parseExtension() { + CloudEvent event = CloudEvent.build() + .withExtension(DistributedTracingExtension.TRACEPARENT, "parent") + .withExtension(DistributedTracingExtension.TRACESTATE, "state") + .build(); + + DistributedTracingExtension tracing = ExtensionsParser + .getInstance() + .parseExtension(DistributedTracingExtension.class, event); + + assertThat(tracing).isNotNull(); + assertThat(tracing.getTraceparent()).isEqualTo("parent"); + assertThat(tracing.getTracestate()).isEqualTo("state"); + } } diff --git a/api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java b/api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java new file mode 100644 index 000000000..f0017025a --- /dev/null +++ b/api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java @@ -0,0 +1,22 @@ +package io.cloudevents.format.json; + +import org.junit.jupiter.api.Test; + +class JsonFormatTest { + + @Test + void serializeToBytes() { + } + + @Test + void serializeToString() { + } + + @Test + void deserialize() { + } + + @Test + void testDeserialize() { + } +} diff --git a/api/src/test/java/io/cloudevents/json/CustomEventTypesTest.java b/api/src/test/java/io/cloudevents/json/CustomEventTypesTest.java deleted file mode 100644 index 2f8500c28..000000000 --- a/api/src/test/java/io/cloudevents/json/CustomEventTypesTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright 2018 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.json; - -import com.fasterxml.jackson.core.type.TypeReference; -import io.cloudevents.json.types.GlusterVolumeClaim; -import io.cloudevents.v02.CloudEventBuilder; -import io.cloudevents.v02.CloudEventImpl; - -import org.junit.Test; - -import java.io.IOException; -import java.net.URI; -import java.util.Map; -import java.util.UUID; - -import static io.cloudevents.json.Json.MAPPER; -import static org.assertj.core.api.Assertions.assertThat; -public class CustomEventTypesTest { - - @Test - public void testBinding() throws IOException { - - // given - final Map storagePayload = (MAPPER.readValue(Thread.currentThread().getContextClassLoader().getResourceAsStream("pvc.json"), Map.class)); - final CloudEventImpl> storageCloudEventWrapper = CloudEventBuilder.>builder() - .withType("ProvisioningSucceeded") - .withSource(URI.create("/scheduler")) - .withId(UUID.randomUUID().toString()) - .withData(storagePayload) - .build(); - - // when - final String httpSerializedPayload = MAPPER.writeValueAsString(storageCloudEventWrapper); - assertThat(httpSerializedPayload).contains("PersistentVolumeClaim"); - //PARSE into real object, on the other side - final CloudEventImpl event = Json.decodeValue(httpSerializedPayload, new TypeReference>() {}); - - // then - assertThat(event.getData().get()).isNotNull(); - assertThat(event.getData().get().getSpec().getCapacity().get("storage")).isEqualTo("2Gi"); - assertThat(event.getData().get().getSpec().getAccessModes()).containsExactly("ReadWriteMany"); - - } -} diff --git a/api/src/test/java/io/cloudevents/json/types/GlusterVolumeClaim.java b/api/src/test/java/io/cloudevents/json/types/GlusterVolumeClaim.java deleted file mode 100644 index 3e59147d4..000000000 --- a/api/src/test/java/io/cloudevents/json/types/GlusterVolumeClaim.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2018 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.json.types; - -import java.util.List; -import java.util.Map; - -public class GlusterVolumeClaim { - - private String apiVersion = null; - - private String kind = null; - - private Map metadata = null; - - private PVCSpec spec = null; - - public String getApiVersion() { - return apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - - public String getKind() { - return kind; - } - - public void setKind(String kind) { - this.kind = kind; - } - - public Map getMetadata() { - return metadata; - } - - public void setMetadata(Map metadata) { - this.metadata = metadata; - } - - public PVCSpec getSpec() { - return spec; - } - - public void setSpec(PVCSpec spec) { - this.spec = spec; - } - - public static class PVCSpec { - public PVCSpec() { - - } - private Map capacity; - private List accessModes; - private GlusterFS glusterfs; - - public Map getCapacity() { - return capacity; - } - - public void setCapacity(Map capacity) { - this.capacity = capacity; - } - - public List getAccessModes() { - return accessModes; - } - - public void setAccessModes(List accessModes) { - this.accessModes = accessModes; - } - - public GlusterFS getGlusterfs() { - return glusterfs; - } - - public void setGlusterfs(GlusterFS glusterfs) { - this.glusterfs = glusterfs; - } - }; - - public static class GlusterFS { - - public GlusterFS() { - - } - private String endpoints; - private String path; - - public String getEndpoints() { - return endpoints; - } - - public void setEndpoints(String endpoint) { - this.endpoints = endpoint; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - } - -} diff --git a/api/src/test/java/io/cloudevents/json/types/Much.java b/api/src/test/java/io/cloudevents/json/types/Much.java deleted file mode 100644 index 9ebd31d8e..000000000 --- a/api/src/test/java/io/cloudevents/json/types/Much.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.cloudevents.json.types; - -/** - * - * @author fabiojose - * - */ -public class Much { - - private String wow; - - public String getWow() { - return wow; - } - - public void setWow(String wow) { - this.wow = wow; - } - - @Override - public String toString() { - return "" + wow + ""; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((wow == null) ? 0 : wow.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Much other = (Much) obj; - if (wow == null) { - if (other.wow != null) - return false; - } else if (!wow.equals(other.wow)) - return false; - return true; - } -} diff --git a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java index 804645082..a893e20eb 100644 --- a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java +++ b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java @@ -15,13 +15,6 @@ */ package io.cloudevents.v1; -import java.io.InputStream; -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Base64; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import com.fasterxml.jackson.core.type.TypeReference; import io.cloudevents.CloudEvent; import io.cloudevents.extensions.DistributedTracingExtension; @@ -32,11 +25,14 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import java.io.InputStream; +import java.net.URI; +import java.time.ZonedDateTime; +import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.*; /** * @@ -55,8 +51,8 @@ private static InputStream resourceOf(String name) { @Test public void should_encode_right_with_minimal_attrs() { // setup - CloudEvent ce = - CloudEventBuilder.builder() + CloudEvent ce = + CloudEvent.buildV1() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") @@ -80,15 +76,13 @@ public void should_encode_right_with_minimal_attrs() { @Test public void should_have_optional_attrs() { // setup - CloudEvent ce = - CloudEventBuilder.builder() + CloudEvent ce = + CloudEvent.buildV1() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") .withSubject("subject0") - .withData("my-data") + .withData("text/plain", URI.create("/schema"), "my-data") .build(); // act @@ -123,8 +117,8 @@ public void should_serialize_trace_extension() { final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - CloudEvent ce = - CloudEventBuilder.builder() + CloudEvent ce = + CloudEvent.buildV1() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") @@ -144,8 +138,8 @@ public void should_serialize_trace_extension() { @Test public void should_not_serialize_attributes_element() { // setup - CloudEvent ce = - CloudEventBuilder.builder() + CloudEvent ce = + CloudEvent.buildV1() .withId("x10") .withSource(URI.create("/source")) .withType("event-type") @@ -165,7 +159,7 @@ public void should_not_serialize_attributes_element() { @Test public void should_have_type() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -175,7 +169,7 @@ public void should_have_type() { @Test public void should_have_id() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -186,7 +180,7 @@ public void should_have_id() { @Test public void should_have_time() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -196,7 +190,7 @@ public void should_have_time() { @Test public void should_have_source() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -206,7 +200,7 @@ public void should_have_source() { @Test public void should_have_datacontenttype() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -217,7 +211,7 @@ public void should_have_datacontenttype() { @Test public void should_have_dataschema() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -228,7 +222,7 @@ public void should_have_dataschema() { @Test public void should_have_specversion() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); // assert @@ -248,7 +242,7 @@ public void should_throw_when_absent() { @Test public void should_have_tracing_extension() { // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); // assert @@ -263,7 +257,7 @@ public void should_have_custom_extension() { String expected = "extension-value"; // act - CloudEvent ce = + CloudEvent ce = Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); // assert diff --git a/pom.xml b/pom.xml index 7b89d2f0e..af1984cb6 100644 --- a/pom.xml +++ b/pom.xml @@ -68,9 +68,11 @@ api + From d54dcf7f83d08ff8bb68611dcec6e1a1dae9bcb7 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 10:45:04 +0200 Subject: [PATCH 07/21] Added conversion between spec versions Added copy builder Signed-off-by: Francesco Guardiani --- .../main/java/io/cloudevents/Attributes.java | 4 + .../main/java/io/cloudevents/CloudEvent.java | 20 +-- .../main/java/io/cloudevents/Extension.java | 4 +- .../DistributedTracingExtension.java | 16 ++- .../extensions/ExtensionsParser.java | 2 + .../impl/BaseCloudEventBuilder.java | 35 ++--- .../io/cloudevents/impl/CloudEventImpl.java | 31 ++++- .../io/cloudevents/v03/AttributesImpl.java | 30 +++-- .../io/cloudevents/v03/CloudEventBuilder.java | 29 ++++- .../cloudevents/v03/http/AttributeMapper.java | 80 ------------ .../cloudevents/v03/http/ExtensionMapper.java | 65 --------- .../io/cloudevents/v03/http/HeaderMapper.java | 79 ----------- .../io/cloudevents/v03/http/Marshallers.java | 83 ------------ .../cloudevents/v03/http/Unmarshallers.java | 123 ------------------ .../io/cloudevents/v1/AttributesImpl.java | 26 +++- .../io/cloudevents/v1/CloudEventBuilder.java | 20 ++- .../cloudevents/v1/http/AttributeMapper.java | 80 ------------ .../cloudevents/v1/http/ExtensionMapper.java | 50 ------- .../io/cloudevents/v1/http/HeaderMapper.java | 79 ----------- .../io/cloudevents/v1/http/Marshallers.java | 67 ---------- .../io/cloudevents/v1/http/Unmarshallers.java | 122 ----------------- .../DistributedTracingExtensionTest.java | 5 +- 22 files changed, 156 insertions(+), 894 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v03/http/ExtensionMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v03/http/Marshallers.java delete mode 100644 api/src/main/java/io/cloudevents/v03/http/Unmarshallers.java delete mode 100644 api/src/main/java/io/cloudevents/v1/http/AttributeMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v1/http/ExtensionMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java delete mode 100644 api/src/main/java/io/cloudevents/v1/http/Marshallers.java delete mode 100644 api/src/main/java/io/cloudevents/v1/http/Unmarshallers.java diff --git a/api/src/main/java/io/cloudevents/Attributes.java b/api/src/main/java/io/cloudevents/Attributes.java index 5ecbc5a34..257825747 100644 --- a/api/src/main/java/io/cloudevents/Attributes.java +++ b/api/src/main/java/io/cloudevents/Attributes.java @@ -60,4 +60,8 @@ public interface Attributes { Optional getTime(); + Attributes toV03(); + + Attributes toV1(); + } diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 025ed2d49..132c5a481 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -54,23 +54,23 @@ public interface CloudEvent { */ Map getExtensions(); - /** - * Write an extension into this cloud event - * @param e - */ - default void writeExtension(Extension e) { - e.writeToEvent(this); - } + CloudEvent toV03(); - static io.cloudevents.v1.CloudEventBuilder build() { - return buildV1(); - } + CloudEvent toV1(); static io.cloudevents.v1.CloudEventBuilder buildV1() { return new io.cloudevents.v1.CloudEventBuilder(); } + static io.cloudevents.v1.CloudEventBuilder buildV1(CloudEvent event) { + return new io.cloudevents.v1.CloudEventBuilder(event); + } + static io.cloudevents.v03.CloudEventBuilder buildV03() { return new io.cloudevents.v03.CloudEventBuilder(); } + + static io.cloudevents.v03.CloudEventBuilder buildV03(CloudEvent event) { + return new io.cloudevents.v03.CloudEventBuilder(event); + } } diff --git a/api/src/main/java/io/cloudevents/Extension.java b/api/src/main/java/io/cloudevents/Extension.java index f99fd0d76..5f1f8d85f 100644 --- a/api/src/main/java/io/cloudevents/Extension.java +++ b/api/src/main/java/io/cloudevents/Extension.java @@ -1,9 +1,11 @@ package io.cloudevents; +import java.util.Map; + public interface Extension { void readFromEvent(CloudEvent event); - void writeToEvent(CloudEvent event); + Map asMap(); } diff --git a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java index 4b09e54e8..5915696b8 100644 --- a/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java +++ b/api/src/main/java/io/cloudevents/extensions/DistributedTracingExtension.java @@ -3,6 +3,10 @@ import io.cloudevents.CloudEvent; import io.cloudevents.Extension; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + public final class DistributedTracingExtension implements Extension { public static final String TRACEPARENT = "traceparent"; @@ -42,13 +46,11 @@ public void readFromEvent(CloudEvent event) { } @Override - public void writeToEvent(CloudEvent event) { - if (traceparent != null) { - event.getExtensions().put(TRACEPARENT, this.traceparent); - } - if (tracestate != null) { - event.getExtensions().put(TRACESTATE, this.tracestate); - } + public Map asMap() { + HashMap map = new HashMap<>(); + map.put(TRACEPARENT, this.traceparent); + map.put(TRACESTATE, this.tracestate); + return Collections.unmodifiableMap(map); } @Override diff --git a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java index a0d2fa7d8..218151cfd 100644 --- a/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java +++ b/api/src/main/java/io/cloudevents/extensions/ExtensionsParser.java @@ -18,6 +18,8 @@ public static ExtensionsParser getInstance() { private HashMap, Supplier> extensionFactories; + + // TODO SPI in future? private ExtensionsParser() { this.extensionFactories = new HashMap<>(); registerExtension(DistributedTracingExtension.class, DistributedTracingExtension::new); diff --git a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java index 105c59a09..9c1052d13 100644 --- a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java @@ -6,9 +6,7 @@ import io.cloudevents.Extension; import java.net.URI; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; public abstract class BaseCloudEventBuilder, T extends Attributes> { @@ -18,15 +16,25 @@ public abstract class BaseCloudEventBuilder extensions; - private List materializedExtensions; @SuppressWarnings("unchecked") public BaseCloudEventBuilder() { this.self = (B)this; this.extensions = new HashMap<>(); - this.materializedExtensions = new ArrayList<>(); } + @SuppressWarnings("unchecked") + public BaseCloudEventBuilder(CloudEvent event) { + this.self = (B)this; + + CloudEventImpl ev = (CloudEventImpl) event; + this.setAttributes(ev.getAttributes()); + this.data = ev.getRawData(); + this.extensions = new HashMap<>(ev.getExtensions()); + } + + protected abstract void setAttributes(Attributes attributes); + protected abstract B withDataContentType(String contentType); protected abstract B withDataSchema(URI dataSchema); @@ -49,15 +57,15 @@ public B withData(String contentType, JsonNode data) { } public B withData(String contentType, URI dataSchema, String data) { - return withEncodedata(contentType, dataSchema, (Object) data); + return withEncodeData(contentType, dataSchema, (Object) data); } public B withData(String contentType, URI dataSchema, byte[] data) { - return withEncodedata(contentType, dataSchema, (Object) data); + return withEncodeData(contentType, dataSchema, (Object) data); } public B withData(String contentType, URI dataSchema, JsonNode data) { - return withEncodedata(contentType, dataSchema, (Object) data); + return withEncodeData(contentType, dataSchema, (Object) data); } public B withExtension(String key, String value) { @@ -76,19 +84,12 @@ public B withExtension(String key, boolean value) { } public B withExtension(Extension extension) { - this.materializedExtensions.add(extension); + this.extensions.putAll(extension.asMap()); return self; } public CloudEvent build() { - CloudEvent event = new CloudEventImpl(this.buildAttributes(), data, extensions); - - // Write materialized extensions into the event - for (Extension ext : this.materializedExtensions) { - ext.writeToEvent(event); - } - - return event; + return new CloudEventImpl(this.buildAttributes(), data, extensions); } private B withEncodedData(String contentType, Object data) { @@ -97,7 +98,7 @@ private B withEncodedData(String contentType, Object data) { return self; } - private B withEncodedata(String contentType, URI dataSchema, Object data) { + private B withEncodeData(String contentType, URI dataSchema, Object data) { withDataContentType(contentType); withDataSchema(dataSchema); this.data = data; diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index c443591dd..25fb0f677 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -13,14 +13,11 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; @JsonSerialize(using = CloudEventSerializer.class) @JsonDeserialize(using = CloudEventDeserializer.class) -public class CloudEventImpl implements CloudEvent { +public final class CloudEventImpl implements CloudEvent { private final Attributes attributes; private final Object data; @@ -109,6 +106,28 @@ public Optional getDataAsJson() { @Override public Map getExtensions() { - return extensions; + return Collections.unmodifiableMap(extensions); + } + + @Override + public CloudEvent toV03() { + return new CloudEventImpl( + attributes.toV03(), + data, + extensions + ); + } + + @Override + public CloudEvent toV1() { + return new CloudEventImpl( + attributes.toV1(), + data, + extensions + ); + } + + protected Object getRawData() { + return data; } } diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index 7697a8f6d..d527585a3 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -32,21 +32,16 @@ public class AttributesImpl implements Attributes { private final String id; - private final URI source; - private final String type; - private final ZonedDateTime time; private final URI schemaurl; - private final String datacontenttype; - private final String subject; - AttributesImpl(String id, URI source, String type, - ZonedDateTime time, URI schemaurl, - String datacontenttype, String subject) { + public AttributesImpl(String id, URI source, String type, + ZonedDateTime time, URI schemaurl, + String datacontenttype, String subject) { this.id = id; this.source = source; this.type = type; @@ -72,6 +67,25 @@ public String getType() { public Optional getTime() { return Optional.ofNullable(time); } + + @Override + public Attributes toV03() { + return this; + } + + @Override + public Attributes toV1() { + return new io.cloudevents.v1.AttributesImpl( + this.id, + this.source, + this.type, + this.datacontenttype, + this.schemaurl, + this.subject, + this.time + ); + } + public Optional getDataSchema() { return getSchemaUrl(); } diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java index f0ce06d22..7a8b443dd 100644 --- a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java @@ -15,6 +15,8 @@ */ package io.cloudevents.v03; +import io.cloudevents.Attributes; +import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; import java.net.URI; @@ -29,20 +31,35 @@ */ public final class CloudEventBuilder extends BaseCloudEventBuilder { - public CloudEventBuilder() { - super(); - } - private String id; private URI source; - private String type; - private ZonedDateTime time; private URI schemaurl; private String datacontenttype; private String subject; + public CloudEventBuilder() { + super(); + } + + public CloudEventBuilder(CloudEvent event) { + super(event); + } + + @Override + protected void setAttributes(Attributes attributes) { + AttributesImpl attr = (AttributesImpl) attributes.toV03(); + this + .withId(attr.getId()) + .withSource(attr.getSource()) + .withType(attr.getType()); + attr.getDataContentType().ifPresent(this::withDataContentType); + attr.getSchemaUrl().ifPresent(this::withSchemaUrl); + attr.getSubject().ifPresent(this::withSubject); + attr.getTime().ifPresent(this::withTime); + } + public CloudEventBuilder withId(String id) { this.id = id; return this; diff --git a/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java b/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java deleted file mode 100644 index 9e00b2063..000000000 --- a/api/src/main/java/io/cloudevents/v03/http/AttributeMapper.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static java.util.stream.Collectors.toMap; - -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicReference; - -import io.cloudevents.fun.BinaryFormatAttributeMapper; -import io.cloudevents.v03.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.3 - */ -public class AttributeMapper { - private AttributeMapper() {} - - static final String HEADER_PREFIX = "ce-"; - - /** - * Following the signature of {@link BinaryFormatAttributeMapper#map(Map)} - * @param headers Map of HTTP request - * @return Map with spec attributes and values without parsing - * @see ContextAttributes - */ - public static Map map(final Map headers) { - Objects.requireNonNull(headers); - - final AtomicReference>> ct = - new AtomicReference<>(); - - ct.set(Optional.empty()); - - Map result = headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .peek(header -> { - if("content-type".equals(header.getKey())) { - ct.set(Optional.ofNullable(header)); - } - }) - .filter(header -> header.getKey().startsWith(HEADER_PREFIX)) - .map(header -> new SimpleEntry<>(header.getKey() - .substring(HEADER_PREFIX.length()), header.getValue())) - .map(header -> new SimpleEntry<>(header.getKey(), - header.getValue().toString())) - .collect(toMap(Entry::getKey, Entry::getValue)); - - ct.get().ifPresent(contentType -> { - result.put(ContextAttributes.DATACONTENTTYPE.name(), - contentType.getValue().toString()); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v03/http/ExtensionMapper.java b/api/src/main/java/io/cloudevents/v03/http/ExtensionMapper.java deleted file mode 100644 index 4a7a22156..000000000 --- a/api/src/main/java/io/cloudevents/v03/http/ExtensionMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatExtensionMapper; -import io.cloudevents.v03.ContextAttributes; - -/** - * - * @author fabiojose - * @version 0.3 - */ -public class ExtensionMapper { - private ExtensionMapper() {} - - private static final List RESERVED_HEADERS = - ContextAttributes.VALUES.stream() - .map(attribute -> AttributeMapper - .HEADER_PREFIX + attribute) - .collect(Collectors.toList()); - static { - RESERVED_HEADERS.add("content-type"); - }; - - /** - * Following the signature of {@link FormatExtensionMapper} - * @param headers The HTTP headers - * @return The potential extensions without parsing - */ - public static Map map(Map headers) { - Objects.requireNonNull(headers); - - // remove all reserved words and the remaining may be extensions - return - headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue().toString())) - .filter(header -> !RESERVED_HEADERS.contains(header.getKey())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } - -} diff --git a/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java b/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java deleted file mode 100644 index d073dae7a..000000000 --- a/api/src/main/java/io/cloudevents/v03/http/HeaderMapper.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static io.cloudevents.v03.http.AttributeMapper.HEADER_PREFIX; - -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatHeaderMapper; -import io.cloudevents.v03.ContextAttributes; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapper { - private HeaderMapper() {} - - private static final String HTTP_CONTENT_TYPE = "Content-Type"; - - /** - * Following the signature of {@link FormatHeaderMapper} - * @param attributes The map of attributes created by {@link AttributeMapper} - * @param extensions The map of extensions created by {@link ExtensionMapper} - * @return The map of HTTP Headers - */ - public static Map map(Map attributes, - Map extensions) { - Objects.requireNonNull(attributes); - Objects.requireNonNull(extensions); - - Map result = attributes.entrySet() - .stream() - .filter(attribute -> null!= attribute.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .filter(header -> !header.getKey() - .equals(ContextAttributes.DATACONTENTTYPE.name())) - .map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(), - header.getValue())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - result.putAll( - extensions.entrySet() - .stream() - .filter(extension -> null!= extension.getValue()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) - ); - - Optional.ofNullable(attributes - .get(ContextAttributes.DATACONTENTTYPE.name())) - .ifPresent((dct) -> { - result.put(HTTP_CONTENT_TYPE, dct); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v03/http/Marshallers.java b/api/src/main/java/io/cloudevents/v03/http/Marshallers.java deleted file mode 100644 index 1404b12e6..000000000 --- a/api/src/main/java/io/cloudevents/v03/http/Marshallers.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.BinaryMarshaller; -import io.cloudevents.format.StructuredMarshaller; -import io.cloudevents.format.Wire; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.json.Json; -import io.cloudevents.v03.Accessor; -import io.cloudevents.v03.AttributesImpl; -import io.cloudevents.v03.CloudEventImpl; - -/** - * - * @author fabiojose - * @version 0.3 - */ -public class Marshallers { - private Marshallers() {} - - private static final Map NO_HEADERS = - new HashMap(); - - /** - * Builds a Binary Content Mode marshaller to marshal cloud events as JSON for - * HTTP Transport Binding - * - * @param The 'data' type - * @return A step to provide the {@link CloudEventImpl} and marshal as JSON - * @see BinaryMarshaller - */ - public static EventStep binary() { - return - BinaryMarshaller. - builder() - .map(AttributesImpl::marshal) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map) - .map(Json.marshaller()::marshal) - .builder(Wire::new); - } - - /** - * Builds a Structured Content Mode marshaller to marshal cloud event as JSON for - * HTTP Transport Binding - * @param The 'data' type - * @return A step to provider the {@link CloudEventImpl} and marshal as JSON - * @see StructuredMarshaller - */ - public static EventStep structured() { - return - StructuredMarshaller. - builder() - .mime("Content-Type", "application/cloudevents+json") - .map((event) -> { - return Json., String> - marshaller().marshal(event, NO_HEADERS); - }) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map); - } -} diff --git a/api/src/main/java/io/cloudevents/v03/http/Unmarshallers.java b/api/src/main/java/io/cloudevents/v03/http/Unmarshallers.java deleted file mode 100644 index b40ca81ed..000000000 --- a/api/src/main/java/io/cloudevents/v03/http/Unmarshallers.java +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import javax.validation.Validator; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.format.BinaryUnmarshaller; -import io.cloudevents.format.StructuredUnmarshaller; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.json.Json; -import io.cloudevents.v03.AttributesImpl; -import io.cloudevents.v03.CloudEventBuilder; -import io.cloudevents.v03.CloudEventImpl; - -/** - * - * @author fabiojose - * @version 0.3 - */ -public class Unmarshallers { - private Unmarshallers() {} - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type) { - return binary(type, null); - } - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @param validator Provided instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type, Validator validator) { - return - BinaryUnmarshaller.builder() - .map(AttributeMapper::map) - .map(AttributesImpl::unmarshal) - .map("application/json", Json.umarshaller(type)::unmarshal) - .next() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .builder(CloudEventBuilder.builder().withValidator(validator)::build); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData) { - return structured(typeOfData, null); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @param validator Provided instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData, Validator validator) { - - return - StructuredUnmarshaller. - builder() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .map((payload, extensions) -> { - CloudEventImpl event = - Json.> - decodeValue(payload, CloudEventImpl.class, typeOfData); - - CloudEventBuilder builder = - CloudEventBuilder.builder(event); - - extensions.get().forEach(extension -> { - builder.withExtension(extension); - }); - - return builder.withValidator(validator).build(); - }); - } -} diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 6a4f492d6..73ea5cda6 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -31,17 +31,11 @@ public class AttributesImpl implements Attributes { private final String id; - private final URI source; - private final String type; - private final String datacontenttype; - private final URI dataschema; - private final String subject; - private final ZonedDateTime time; public AttributesImpl(String id, URI source, @@ -91,7 +85,25 @@ public Optional getTime() { return Optional.ofNullable(time); } - @Override + @Override + public Attributes toV03() { + return new io.cloudevents.v03.AttributesImpl( + this.id, + this.source, + this.type, + this.time, + this.dataschema, + this.datacontenttype, + this.subject + ); + } + + @Override + public Attributes toV1() { + return this; + } + + @Override public String toString() { return "Attibutes V1.0 [id=" + id + ", source=" + source + ", type=" + type diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index bde664dab..fa0ee921b 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -1,5 +1,7 @@ package io.cloudevents.v1; +import io.cloudevents.Attributes; +import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; import java.net.URI; @@ -15,7 +17,6 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder map(final Map headers) { - Objects.requireNonNull(headers); - - final AtomicReference>> ct = - new AtomicReference<>(); - - ct.set(Optional.empty()); - - Map result = headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .peek(header -> { - if("content-type".equals(header.getKey())) { - ct.set(Optional.ofNullable(header)); - } - }) - .filter(header -> header.getKey().startsWith(HEADER_PREFIX)) - .map(header -> new SimpleEntry<>(header.getKey() - .substring(HEADER_PREFIX.length()), header.getValue())) - .map(header -> new SimpleEntry<>(header.getKey(), - header.getValue().toString())) - .collect(toMap(Entry::getKey, Entry::getValue)); - - ct.get().ifPresent(contentType -> { - result.put(ContextAttributes.DATACONTENTTYPE.name(), - contentType.getValue().toString()); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v1/http/ExtensionMapper.java b/api/src/main/java/io/cloudevents/v1/http/ExtensionMapper.java deleted file mode 100644 index ff4c7a28a..000000000 --- a/api/src/main/java/io/cloudevents/v1/http/ExtensionMapper.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.cloudevents.v1.http; - -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatExtensionMapper; -import io.cloudevents.v1.ContextAttributes; -import io.cloudevents.v1.http.AttributeMapper; - -/** - * - * @author fabiojose - * @version 1.0 - */ -public class ExtensionMapper { - private ExtensionMapper() {} - - private static final List RESERVED_HEADERS = - ContextAttributes.VALUES.stream() - .map(attribute -> AttributeMapper - .HEADER_PREFIX + attribute) - .collect(Collectors.toList()); - static { - RESERVED_HEADERS.add("content-type"); - }; - - /** - * Following the signature of {@link FormatExtensionMapper} - * @param headers The HTTP headers - * @return The potential extensions without parsing - */ - public static Map map(Map headers) { - Objects.requireNonNull(headers); - - // remove all reserved words and the remaining may be extensions - return - headers.entrySet() - .stream() - .filter(header -> null!= header.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue().toString())) - .filter(header -> !RESERVED_HEADERS.contains(header.getKey())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } -} diff --git a/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java b/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java deleted file mode 100644 index 201ff8959..000000000 --- a/api/src/main/java/io/cloudevents/v1/http/HeaderMapper.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static io.cloudevents.v1.http.AttributeMapper.HEADER_PREFIX; - -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import io.cloudevents.fun.FormatHeaderMapper; -import io.cloudevents.v1.ContextAttributes; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapper { - private HeaderMapper() {} - - private static final String HTTP_CONTENT_TYPE = "Content-Type"; - - /** - * Following the signature of {@link FormatHeaderMapper} - * @param attributes The map of attributes created by {@link AttributeMapper} - * @param extensions The map of extensions created by {@link ExtensionMapper} - * @return The map of HTTP Headers - */ - public static Map map(Map attributes, - Map extensions) { - Objects.requireNonNull(attributes); - Objects.requireNonNull(extensions); - - Map result = attributes.entrySet() - .stream() - .filter(attribute -> null!= attribute.getValue()) - .map(header -> new SimpleEntry<>(header.getKey() - .toLowerCase(Locale.US), header.getValue())) - .filter(header -> !header.getKey() - .equals(ContextAttributes.DATACONTENTTYPE.name())) - .map(header -> new SimpleEntry<>(HEADER_PREFIX + header.getKey(), - header.getValue())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - result.putAll( - extensions.entrySet() - .stream() - .filter(extension -> null != extension.getValue()) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) - ); - - Optional.ofNullable(attributes - .get(ContextAttributes.DATACONTENTTYPE.name())) - .ifPresent((dct) -> { - result.put(HTTP_CONTENT_TYPE, dct); - }); - - return result; - } - -} diff --git a/api/src/main/java/io/cloudevents/v1/http/Marshallers.java b/api/src/main/java/io/cloudevents/v1/http/Marshallers.java deleted file mode 100644 index 1bb8a69f8..000000000 --- a/api/src/main/java/io/cloudevents/v1/http/Marshallers.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.cloudevents.v1.http; - -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.BinaryMarshaller; -import io.cloudevents.format.StructuredMarshaller; -import io.cloudevents.format.Wire; -import io.cloudevents.format.builder.EventStep; -import io.cloudevents.json.Json; -import io.cloudevents.v1.Accessor; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventImpl; -import io.cloudevents.v1.http.HeaderMapper; - -/** - * - * @author fabiojose - * @version 1.0 - */ -public class Marshallers { - private Marshallers() {} - - private static final Map NO_HEADERS = - new HashMap(); - - /** - * Builds a Binary Content Mode marshaller to marshal cloud events as JSON for - * HTTP Transport Binding - * - * @param The 'data' type - * @return A step to provide the {@link CloudEventImpl} and marshal as JSON - * @see BinaryMarshaller - */ - public static EventStep binary() { - return - BinaryMarshaller. - builder() - .map(AttributesImpl::marshal) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map) - .map(Json.marshaller()::marshal) - .builder(Wire::new); - } - - /** - * Builds a Structured Content Mode marshaller to marshal cloud event as JSON for - * HTTP Transport Binding - * @param The 'data' type - * @return A step to provider the {@link CloudEventImpl} and marshal as JSON - * @see StructuredMarshaller - */ - public static EventStep structured() { - return - StructuredMarshaller. - builder() - .mime("Content-Type", "application/cloudevents+json") - .map((event) -> Json., String> - marshaller().marshal(event, NO_HEADERS)) - .map(Accessor::extensionsOf) - .map(ExtensionFormat::marshal) - .map(HeaderMapper::map); - } -} diff --git a/api/src/main/java/io/cloudevents/v1/http/Unmarshallers.java b/api/src/main/java/io/cloudevents/v1/http/Unmarshallers.java deleted file mode 100644 index 90412c327..000000000 --- a/api/src/main/java/io/cloudevents/v1/http/Unmarshallers.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import javax.validation.Validator; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.format.BinaryUnmarshaller; -import io.cloudevents.format.StructuredUnmarshaller; -import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.json.Json; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; - -/** - * - * @author fabiojose - * @version 1.0 - */ -public class Unmarshallers { - private Unmarshallers() {} - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type) { - return binary(type, null); - } - - /** - * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param type The type reference to use for 'data' unmarshal - * @param validator Provided instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see BinaryUnmarshaller - */ - public static HeadersStep - binary(Class type, Validator validator) { - return - BinaryUnmarshaller.builder() - .map(AttributeMapper::map) - .map(AttributesImpl::unmarshal) - .map("application/json", Json.umarshaller(type)::unmarshal) - .next() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .builder(CloudEventBuilder.builder().withValidator(validator)::build); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData) { - return structured(typeOfData, null); - } - - /** - * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data - * for HTTP Transport Binding - * - * @param The 'data' type - * @param typeOfData The type reference to use for 'data' unmarshal - * @param validator Provided instance of a {@link Validator} - * @return A step to supply the headers, payload and to unmarshal - * @see StructuredUnmarshaller - */ - public static HeadersStep - structured(Class typeOfData, Validator validator) { - return - StructuredUnmarshaller. - builder() - .map(ExtensionMapper::map) - .map(DistributedTracingExtension::unmarshall) - .next() - .map((payload, extensions) -> { - CloudEventImpl event = - Json.> - decodeValue(payload, CloudEventImpl.class, typeOfData); - - CloudEventBuilder builder = - CloudEventBuilder.builder(event); - - extensions.get().forEach(extension -> { - builder.withExtension(extension); - }); - - return builder.withValidator(validator).build(); - }); - } -} diff --git a/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java b/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java index e58cbc7db..f277191db 100644 --- a/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java +++ b/api/src/test/java/io/cloudevents/extensions/DistributedTracingExtensionTest.java @@ -33,8 +33,7 @@ public void writeExtension() { tracing.setTraceparent("parent"); tracing.setTracestate("state"); - CloudEvent event = CloudEvent.build().build(); - tracing.writeToEvent(event); + CloudEvent event = CloudEvent.buildV1().withExtension(tracing).build(); assertThat(event.getExtensions()) .containsEntry(DistributedTracingExtension.TRACEPARENT, "parent") @@ -43,7 +42,7 @@ public void writeExtension() { @Test public void parseExtension() { - CloudEvent event = CloudEvent.build() + CloudEvent event = CloudEvent.buildV1() .withExtension(DistributedTracingExtension.TRACEPARENT, "parent") .withExtension(DistributedTracingExtension.TRACESTATE, "state") .build(); From 223d786a3f86aba706a5d0e3779cd861ed5285b6 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 12:30:55 +0200 Subject: [PATCH 08/21] Message interfaces should be fine now Signed-off-by: Francesco Guardiani --- api/README.md | 26 +---- .../impl/BaseCloudEventBuilder.java | 28 +++++- .../io/cloudevents/message/BinaryMessage.java | 10 +- .../message/BinaryMessageAttributes.java | 12 +++ .../message/BinaryMessageVisitor.java | 9 +- .../message/BinaryMessageVisitorFactory.java | 8 ++ .../java/io/cloudevents/message/Encoding.java | 4 +- .../java/io/cloudevents/message/Message.java | 6 +- .../message/MessageVisitException.java | 11 ++- .../message/StructuredMessage.java | 5 +- .../message/StructuredMessageVisitor.java | 5 +- .../io/cloudevents/v03/AttributesImpl.java | 95 ++++++++++++++----- .../io/cloudevents/v03/CloudEventBuilder.java | 18 ++++ .../io/cloudevents/v03/ContextAttributes.java | 4 + .../io/cloudevents/v1/AttributesImpl.java | 75 +++++++++++---- .../io/cloudevents/v1/CloudEventBuilder.java | 18 ++++ .../io/cloudevents/v1/ContextAttributes.java | 4 + 17 files changed, 263 insertions(+), 75 deletions(-) create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessageAttributes.java create mode 100644 api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java diff --git a/api/README.md b/api/README.md index eff4bcc39..01dfb6706 100644 --- a/api/README.md +++ b/api/README.md @@ -11,13 +11,10 @@ Here we will see how to use the pre-configure marshallers and unmarshallers. The high-level API to marshal CloudEvents as binary content mode. ```java -import java.net.URI; -import java.time.ZonedDateTime; -import io.cloudevents.extensions.DistributedTracingExtension; + import io.cloudevents.extensions.ExtensionFormat; import io.cloudevents.format.Wire; -import io.cloudevents.v1.CloudEventBuilder; import io.cloudevents.v1.CloudEventImpl; import io.cloudevents.v1.http.Marshallers; @@ -39,7 +36,7 @@ CloudEventImpl ce = CloudEventBuilder.builder() .withType("com.github.pull.create") .withSource(URI.create("https://github.com/cloudevents/spec/pull")) - .withId("A234-1234-1234") + .withId("A234-1234-1234") .withDataschema(URI.create("http://my.br")) .withTime(ZonedDateTime.now()) .withDataContentType("text/plain") @@ -69,13 +66,8 @@ wire.getPayload(); //Optional which has the JSON The high-level API to unmarshal CloudEvents from binary content mode. ```java -import java.util.HashMap; -import java.util.Map; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventBuilder; + import io.cloudevents.v1.http.Unmarshallers; // . . . @@ -114,12 +106,9 @@ event.getExtensions(); The high-level API to marshal CloudEvents as structured content mode. ```java -import java.net.URI; -import java.time.ZonedDateTime; -import io.cloudevents.extensions.DistributedTracingExtension; + import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.v1.CloudEventBuilder; import io.cloudevents.v1.CloudEventImpl; import io.cloudevents.v1.http.Marshallers; @@ -382,14 +371,9 @@ Wire wire = /* * The imports used by the example bellow */ -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; import io.cloudevents.format.BinaryUnmarshaller; import io.cloudevents.format.builder.HeadersStep; -import io.cloudevents.json.Json; import io.cloudevents.json.types.Much; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventBuilder; // . . . @@ -520,7 +504,7 @@ HeadersStep step = * - now we get the HeadersStep, a common step that event unmarshaller must returns * - from here we just call withHeaders(), withPayload() and unmarshal() */ - .map((payload, extensions) -> { + .map((payload, extensions) -> { CloudEventImpl event = Json.> decodeValue(payload, CloudEventImpl.class, Much.class); diff --git a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java index 9c1052d13..a8186a69c 100644 --- a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java @@ -4,12 +4,14 @@ import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.Extension; +import io.cloudevents.message.BinaryMessageVisitor; +import io.cloudevents.message.MessageVisitException; import java.net.URI; import java.util.HashMap; import java.util.Map; -public abstract class BaseCloudEventBuilder, T extends Attributes> { +public abstract class BaseCloudEventBuilder, T extends Attributes> implements BinaryMessageVisitor { // This is a little trick for enabling fluency private B self; @@ -105,4 +107,28 @@ private B withEncodeData(String contentType, URI dataSchema, Object data) { return self; } + @Override + public void setExtension(String name, String value) throws MessageVisitException { + this.withExtension(name, value); + } + + @Override + public void setExtension(String name, Number value) throws MessageVisitException { + this.withExtension(name, value); + } + + @Override + public void setExtension(String name, Boolean value) throws MessageVisitException { + this.withExtension(name, value); + } + + @Override + public void setBody(byte[] value) throws MessageVisitException { + this.data = value; + } + + @Override + public CloudEvent end() { + return build(); + } } diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessage.java b/api/src/main/java/io/cloudevents/message/BinaryMessage.java index 28b4390a4..ff0eef1c3 100644 --- a/api/src/main/java/io/cloudevents/message/BinaryMessage.java +++ b/api/src/main/java/io/cloudevents/message/BinaryMessage.java @@ -11,10 +11,16 @@ public interface BinaryMessage { * @throws MessageVisitException * @throws IllegalStateException If the message is not a valid binary message */ - void visit(BinaryMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + , V> V visit(BinaryMessageVisitorFactory visitor) throws MessageVisitException, IllegalStateException; default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { - //TODO + return this.visit(specVersion -> { + switch (specVersion) { + case V1: return CloudEvent.buildV1(); + case V03: return CloudEvent.buildV03(); + } + return null; // This can never happen + }); }; } diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageAttributes.java b/api/src/main/java/io/cloudevents/message/BinaryMessageAttributes.java new file mode 100644 index 000000000..584540ed3 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageAttributes.java @@ -0,0 +1,12 @@ +package io.cloudevents.message; + +public interface BinaryMessageAttributes { + + /** + * + * @param visitor + * @throws MessageVisitException + */ + void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException; + +} diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java index b4e236820..f0361674c 100644 --- a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitor.java @@ -1,12 +1,11 @@ package io.cloudevents.message; -import io.cloudevents.SpecVersion; - -public interface BinaryMessageVisitor extends BinaryMessageAttributesVisitor, BinaryMessageExtensionsVisitor { - - void setSpecVersion(SpecVersion specVersion) throws MessageVisitException; +public interface BinaryMessageVisitor extends BinaryMessageAttributesVisitor, BinaryMessageExtensionsVisitor { // TODO one day we'll convert this to some byte stream void setBody(byte[] value) throws MessageVisitException; + // Returns an eventual output value + V end(); + } diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java new file mode 100644 index 000000000..309999638 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java @@ -0,0 +1,8 @@ +package io.cloudevents.message; + +import io.cloudevents.SpecVersion; + +import java.util.function.Function; + +@FunctionalInterface +public interface BinaryMessageVisitorFactory, V> extends Function { } diff --git a/api/src/main/java/io/cloudevents/message/Encoding.java b/api/src/main/java/io/cloudevents/message/Encoding.java index 3fb474db1..c173d980e 100644 --- a/api/src/main/java/io/cloudevents/message/Encoding.java +++ b/api/src/main/java/io/cloudevents/message/Encoding.java @@ -3,5 +3,7 @@ public enum Encoding { STRUCTURED, BINARY, - UNKNOWN + UNKNOWN; + + public static IllegalStateException UNKNOWN_ENCODING_EXCEPTION = new IllegalStateException("Unknown encoding"); } diff --git a/api/src/main/java/io/cloudevents/message/Message.java b/api/src/main/java/io/cloudevents/message/Message.java index 38adf7013..99d29e54b 100644 --- a/api/src/main/java/io/cloudevents/message/Message.java +++ b/api/src/main/java/io/cloudevents/message/Message.java @@ -7,7 +7,11 @@ public interface Message extends StructuredMessage, BinaryMessage { Encoding getEncoding(); default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { - //TODO + switch (getEncoding()) { + case BINARY: return ((BinaryMessage)this).toEvent(); + case STRUCTURED: return ((StructuredMessage)this).toEvent(); + default: throw Encoding.UNKNOWN_ENCODING_EXCEPTION; + } }; } diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitException.java b/api/src/main/java/io/cloudevents/message/MessageVisitException.java index e01bcf142..53cfb3e27 100644 --- a/api/src/main/java/io/cloudevents/message/MessageVisitException.java +++ b/api/src/main/java/io/cloudevents/message/MessageVisitException.java @@ -3,7 +3,8 @@ public class MessageVisitException extends RuntimeException { public enum MessageVisitExceptionKind { - INVALID_ATTRIBUTE_TYPE + INVALID_ATTRIBUTE_TYPE, + INVALID_SPEC_VERSION, } private MessageVisitExceptionKind kind; @@ -29,4 +30,12 @@ public static MessageVisitException newInvalidAttributeType(String attributeName ); } + public static MessageVisitException newInvalidSpecVersion(String specVersion) { + return new MessageVisitException( + MessageVisitExceptionKind.INVALID_ATTRIBUTE_TYPE, + "Invalid specversion: " + specVersion + ); + } + + } diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessage.java b/api/src/main/java/io/cloudevents/message/StructuredMessage.java index cc579e513..c298abc43 100644 --- a/api/src/main/java/io/cloudevents/message/StructuredMessage.java +++ b/api/src/main/java/io/cloudevents/message/StructuredMessage.java @@ -1,6 +1,7 @@ package io.cloudevents.message; import io.cloudevents.CloudEvent; +import io.cloudevents.format.EventFormat; @FunctionalInterface public interface StructuredMessage { @@ -11,10 +12,10 @@ public interface StructuredMessage { * @throws MessageVisitException * @throws IllegalStateException If the message is not a valid structured message */ - void visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException; + T visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException; default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { - //TODO + return this.visit(EventFormat::deserialize); }; } diff --git a/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java index ffff5c322..193b0a059 100644 --- a/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java +++ b/api/src/main/java/io/cloudevents/message/StructuredMessageVisitor.java @@ -2,9 +2,10 @@ import io.cloudevents.format.EventFormat; -public interface StructuredMessageVisitor { +@FunctionalInterface +public interface StructuredMessageVisitor { // TODO one day we'll convert this to some byte stream - void setEvent(EventFormat format, byte[] value) throws MessageVisitException; + T setEvent(EventFormat format, byte[] value) throws MessageVisitException; } diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index d527585a3..364a0c82e 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -17,6 +17,9 @@ import io.cloudevents.Attributes; import io.cloudevents.SpecVersion; +import io.cloudevents.message.BinaryMessageAttributes; +import io.cloudevents.message.BinaryMessageAttributesVisitor; +import io.cloudevents.message.MessageVisitException; import java.net.URI; import java.time.ZonedDateTime; @@ -29,15 +32,15 @@ * @author slinkydeveloper * */ -public class AttributesImpl implements Attributes { +public class AttributesImpl implements Attributes, BinaryMessageAttributes { private final String id; private final URI source; private final String type; - private final ZonedDateTime time; - private final URI schemaurl; private final String datacontenttype; - private final String subject; + private final URI schemaurl; + private final String subject; + private final ZonedDateTime time; public AttributesImpl(String id, URI source, String type, ZonedDateTime time, URI schemaurl, @@ -52,21 +55,80 @@ public AttributesImpl(String id, URI source, String type, this.subject = subject; } + public SpecVersion getSpecVersion() { + return SpecVersion.V03; + } + public String getId() { return id; } + public URI getSource() { return source; } - public SpecVersion getSpecVersion() { - return SpecVersion.V03; - } + public String getType() { return type; } - public Optional getTime() { - return Optional.ofNullable(time); - } + + public Optional getDataContentType() { + return Optional.ofNullable(datacontenttype); + } + public Optional getDataSchema() { + return getSchemaUrl(); + } + + public Optional getSchemaUrl() { + return Optional.ofNullable(schemaurl); + } + + public Optional getSubject() { + return Optional.ofNullable(subject); + } + + public Optional getTime() { + return Optional.ofNullable(time); + } + + @Override + public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException { + visitor.setAttribute( + ContextAttributes.ID.name().toLowerCase(), + this.id + ); + visitor.setAttribute( + ContextAttributes.SOURCE.name().toLowerCase(), + this.source + ); + visitor.setAttribute( + ContextAttributes.TYPE.name().toLowerCase(), + this.type + ); + if (this.datacontenttype != null) { + visitor.setAttribute( + ContextAttributes.DATACONTENTTYPE.name().toLowerCase(), + this.datacontenttype + ); + } + if (this.schemaurl != null) { + visitor.setAttribute( + ContextAttributes.SCHEMAURL.name().toLowerCase(), + this.schemaurl + ); + } + if (this.subject != null) { + visitor.setAttribute( + ContextAttributes.SUBJECT.name().toLowerCase(), + this.subject + ); + } + if (this.time != null) { + visitor.setAttribute( + ContextAttributes.TYPE.name().toLowerCase(), + this.time + ); + } + } @Override public Attributes toV03() { @@ -86,19 +148,6 @@ public Attributes toV1() { ); } - public Optional getDataSchema() { - return getSchemaUrl(); - } - public Optional getSchemaUrl() { - return Optional.ofNullable(schemaurl); - } - public Optional getDataContentType() { - return Optional.ofNullable(datacontenttype); - } - public Optional getSubject() { - return Optional.ofNullable(subject); - } - @Override public String toString() { return "AttributesImpl [id=" + id + ", source=" + source diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java index 7a8b443dd..310e0a20f 100644 --- a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java @@ -18,6 +18,7 @@ import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; +import io.cloudevents.message.MessageVisitException; import java.net.URI; import java.time.ZonedDateTime; @@ -106,4 +107,21 @@ protected CloudEventBuilder withDataSchema(URI dataSchema) { protected AttributesImpl buildAttributes() { return new AttributesImpl(id, source, type, time, schemaurl, datacontenttype, subject); } + + // Message impl + + @Override + public void setAttribute(String name, String value) throws MessageVisitException { + + } + + @Override + public void setAttribute(String name, URI value) throws MessageVisitException { + + } + + @Override + public void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { + + } } diff --git a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java index 3c35ee2f9..f14414223 100644 --- a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java @@ -40,4 +40,8 @@ public enum ContextAttributes { .map(Enum::name) .map(String::toLowerCase) .collect(Collectors.toList()); + + public static ContextAttributes parse(String value) { + return ContextAttributes.valueOf(value.toUpperCase()); + } } diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 73ea5cda6..2f16cf678 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -17,6 +17,9 @@ import io.cloudevents.Attributes; import io.cloudevents.SpecVersion; +import io.cloudevents.message.BinaryMessageAttributes; +import io.cloudevents.message.BinaryMessageAttributesVisitor; +import io.cloudevents.message.MessageVisitException; import java.net.URI; import java.time.ZonedDateTime; @@ -28,7 +31,7 @@ * @author slinkydeveloper * @version 1.0 */ -public class AttributesImpl implements Attributes { +public class AttributesImpl implements Attributes, BinaryMessageAttributes { private final String id; private final URI source; @@ -51,10 +54,9 @@ public AttributesImpl(String id, URI source, this.time = time; } - @Override - public Optional getDataContentType() { - return Optional.ofNullable(datacontenttype); - } + public SpecVersion getSpecVersion() { + return SpecVersion.V1; + } public String getId() { return id; @@ -64,14 +66,15 @@ public URI getSource() { return source; } - public SpecVersion getSpecVersion() { - return SpecVersion.V1; - } - public String getType() { return type; } + @Override + public Optional getDataContentType() { + return Optional.ofNullable(datacontenttype); + } + @Override public Optional getDataSchema() { return Optional.ofNullable(dataschema); @@ -104,13 +107,53 @@ public Attributes toV1() { } @Override - public String toString() { - return "Attibutes V1.0 [id=" + id + ", source=" + source - + ", type=" + type - + ", datacontenttype=" + datacontenttype + ", dataschema=" - + dataschema + ", subject=" + subject - + ", time=" + time + "]"; - } + public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitException { + visitor.setAttribute( + ContextAttributes.ID.name().toLowerCase(), + this.id + ); + visitor.setAttribute( + ContextAttributes.SOURCE.name().toLowerCase(), + this.source + ); + visitor.setAttribute( + ContextAttributes.TYPE.name().toLowerCase(), + this.type + ); + if (this.datacontenttype != null) { + visitor.setAttribute( + ContextAttributes.DATACONTENTTYPE.name().toLowerCase(), + this.datacontenttype + ); + } + if (this.dataschema != null) { + visitor.setAttribute( + ContextAttributes.DATASCHEMA.name().toLowerCase(), + this.dataschema + ); + } + if (this.subject != null) { + visitor.setAttribute( + ContextAttributes.SUBJECT.name().toLowerCase(), + this.subject + ); + } + if (this.time != null) { + visitor.setAttribute( + ContextAttributes.TYPE.name().toLowerCase(), + this.time + ); + } + } + + @Override + public String toString() { + return "Attibutes V1.0 [id=" + id + ", source=" + source + + ", type=" + type + + ", datacontenttype=" + datacontenttype + ", dataschema=" + + dataschema + ", subject=" + subject + + ", time=" + time + "]"; + } // /** // * Used by the Jackson framework to unmarshall. diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index fa0ee921b..70a6f1f5a 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -3,6 +3,7 @@ import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; +import io.cloudevents.message.MessageVisitException; import java.net.URI; import java.time.ZonedDateTime; @@ -84,4 +85,21 @@ public CloudEventBuilder withTime(ZonedDateTime time) { protected AttributesImpl buildAttributes() { return new AttributesImpl(id, source, type, datacontenttype, dataschema, subject, time); } + + // Message impl + + @Override + public void setAttribute(String name, String value) throws MessageVisitException { + + } + + @Override + public void setAttribute(String name, URI value) throws MessageVisitException { + + } + + @Override + public void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { + + } } diff --git a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java index ee67a91a7..530c66f21 100644 --- a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java @@ -38,4 +38,8 @@ public enum ContextAttributes { .map(Enum::name) .map(String::toLowerCase) .collect(Collectors.toList()); + + public static ContextAttributes parse(String value) { + return ContextAttributes.valueOf(value.toUpperCase()); + } } From 8f1b8d2da99af5a0b50470d3ef4bd6bf9723ba3b Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 13:21:54 +0200 Subject: [PATCH 09/21] Completed implementation of message interfaces for events. Signed-off-by: Francesco Guardiani --- .../main/java/io/cloudevents/CloudEvent.java | 7 +++ .../cloudevents/fun/AttributeMarshaller.java | 35 ------------ .../fun/AttributeUnmarshaller.java | 35 ------------ .../fun/BinaryFormatAttributeMapper.java | 35 ------------ .../io/cloudevents/fun/DataMarshaller.java | 40 ------------- .../io/cloudevents/fun/DataUnmarshaller.java | 40 ------------- .../cloudevents/fun/EnvelopeMarshaller.java | 35 ------------ .../cloudevents/fun/EnvelopeUnmarshaller.java | 42 -------------- .../fun/ExtensionFormatAccessor.java | 25 --------- .../cloudevents/fun/ExtensionMarshaller.java | 38 ------------- .../cloudevents/fun/ExtensionUmarshaller.java | 38 ------------- .../fun/FormatExtensionMapper.java | 34 ----------- .../cloudevents/fun/FormatHeaderMapper.java | 38 ------------- .../java/io/cloudevents/fun/WireBuilder.java | 38 ------------- .../cloudevents/impl/AttributesInternal.java | 6 ++ .../io/cloudevents/impl/CloudEventImpl.java | 54 ++++++++++++++++-- .../message/MessageVisitException.java | 33 +++++++++-- .../main/java/io/cloudevents/types/Time.java | 6 ++ .../io/cloudevents/v03/AttributesImpl.java | 4 +- .../io/cloudevents/v03/CloudEventBuilder.java | 56 ++++++++++++++++++- .../io/cloudevents/v03/ContextAttributes.java | 5 ++ .../io/cloudevents/v1/AttributesImpl.java | 4 +- .../io/cloudevents/v1/CloudEventBuilder.java | 56 ++++++++++++++++++- .../io/cloudevents/v1/ContextAttributes.java | 8 ++- 24 files changed, 218 insertions(+), 494 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/fun/AttributeMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/AttributeUnmarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/BinaryFormatAttributeMapper.java delete mode 100644 api/src/main/java/io/cloudevents/fun/DataMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/DataUnmarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/EnvelopeMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/EnvelopeUnmarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/ExtensionFormatAccessor.java delete mode 100644 api/src/main/java/io/cloudevents/fun/ExtensionMarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/ExtensionUmarshaller.java delete mode 100644 api/src/main/java/io/cloudevents/fun/FormatExtensionMapper.java delete mode 100644 api/src/main/java/io/cloudevents/fun/FormatHeaderMapper.java delete mode 100644 api/src/main/java/io/cloudevents/fun/WireBuilder.java create mode 100644 api/src/main/java/io/cloudevents/impl/AttributesInternal.java diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 132c5a481..098563041 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -16,6 +16,9 @@ package io.cloudevents; import com.fasterxml.jackson.databind.JsonNode; +import io.cloudevents.format.EventFormat; +import io.cloudevents.message.BinaryMessage; +import io.cloudevents.message.StructuredMessage; import java.util.Map; import java.util.Optional; @@ -58,6 +61,10 @@ public interface CloudEvent { CloudEvent toV1(); + BinaryMessage asBinaryMessage(); + + StructuredMessage asStructuredMessage(EventFormat format); + static io.cloudevents.v1.CloudEventBuilder buildV1() { return new io.cloudevents.v1.CloudEventBuilder(); } diff --git a/api/src/main/java/io/cloudevents/fun/AttributeMarshaller.java b/api/src/main/java/io/cloudevents/fun/AttributeMarshaller.java deleted file mode 100644 index e0c81af29..000000000 --- a/api/src/main/java/io/cloudevents/fun/AttributeMarshaller.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -import io.cloudevents.Attributes; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface AttributeMarshaller { - - /** - * Marshals the {@link Attributes} to map of attributes. - */ - Map marshal(A attributes); - -} diff --git a/api/src/main/java/io/cloudevents/fun/AttributeUnmarshaller.java b/api/src/main/java/io/cloudevents/fun/AttributeUnmarshaller.java deleted file mode 100644 index 0f2b302bb..000000000 --- a/api/src/main/java/io/cloudevents/fun/AttributeUnmarshaller.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -import io.cloudevents.Attributes; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface AttributeUnmarshaller { - - /** - * Unmarshals the map of CloudEvent attributes into actual ones - */ - A unmarshal(Map attributes); - -} diff --git a/api/src/main/java/io/cloudevents/fun/BinaryFormatAttributeMapper.java b/api/src/main/java/io/cloudevents/fun/BinaryFormatAttributeMapper.java deleted file mode 100644 index 9fe1be61b..000000000 --- a/api/src/main/java/io/cloudevents/fun/BinaryFormatAttributeMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface BinaryFormatAttributeMapper { - - /** - * Maps the 'headers' of binary format into CloudEvent attributes - * @param headers - * @return - */ - Map map(Map headers); - -} diff --git a/api/src/main/java/io/cloudevents/fun/DataMarshaller.java b/api/src/main/java/io/cloudevents/fun/DataMarshaller.java deleted file mode 100644 index 5cec7d024..000000000 --- a/api/src/main/java/io/cloudevents/fun/DataMarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -/** - * - * @author fabiojose - * - * @param

The payload type - * @param The 'data' type - * @param The type of headers value - */ -@FunctionalInterface -public interface DataMarshaller { - - /** - * Marshals the 'data' into payload - * @param data - * @param headers - * @return - * @throws RuntimeException When something bad happens during the marshal process - */ - P marshal(T data, Map headers) throws RuntimeException; - -} diff --git a/api/src/main/java/io/cloudevents/fun/DataUnmarshaller.java b/api/src/main/java/io/cloudevents/fun/DataUnmarshaller.java deleted file mode 100644 index 55c095458..000000000 --- a/api/src/main/java/io/cloudevents/fun/DataUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import io.cloudevents.Attributes; - -/** - * For the 'data' unmarshalling. - * - * @author fabiojose - * - * @param

The payload type - * @param The 'data' type - */ -@FunctionalInterface -public interface DataUnmarshaller { - - /** - * Unmarshals the payload into 'data' - * @param payload - * @param attributes - * @return - * @throws RuntimeException If something bad happens during the umarshal - */ - T unmarshal(P payload, A attributes); - -} diff --git a/api/src/main/java/io/cloudevents/fun/EnvelopeMarshaller.java b/api/src/main/java/io/cloudevents/fun/EnvelopeMarshaller.java deleted file mode 100644 index f8f86db6d..000000000 --- a/api/src/main/java/io/cloudevents/fun/EnvelopeMarshaller.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface EnvelopeMarshaller { - - /** - * Marshals a CloudEvent instance into the wire format - * @param event The CloudEvents instance - * @return the wire format - */ - P marshal(CloudEvent event); -} diff --git a/api/src/main/java/io/cloudevents/fun/EnvelopeUnmarshaller.java b/api/src/main/java/io/cloudevents/fun/EnvelopeUnmarshaller.java deleted file mode 100644 index 96de5ef2f..000000000 --- a/api/src/main/java/io/cloudevents/fun/EnvelopeUnmarshaller.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.List; -import java.util.function.Supplier; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; - -/** - * - * @author fabiojose - * - */ -public interface EnvelopeUnmarshaller { - - /** - * Unmarshals the payload into {@link CloudEvent} instance implementation - * - * @param payload The envelope payload - * @param extensions Supplies a list of {@link ExtensionFormat} - * @return The unmarshalled impl of CloudEvent - */ - CloudEvent unmarshal(P payload, - Supplier> extensions); - -} diff --git a/api/src/main/java/io/cloudevents/fun/ExtensionFormatAccessor.java b/api/src/main/java/io/cloudevents/fun/ExtensionFormatAccessor.java deleted file mode 100644 index 03ef43296..000000000 --- a/api/src/main/java/io/cloudevents/fun/ExtensionFormatAccessor.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.cloudevents.fun; - -import java.util.Collection; - -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.ExtensionFormat; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface ExtensionFormatAccessor { - - /** - * To get access to the internal collection of {@link ExtensionFormat} inside - * the {@link CloudEvent} implementation - * - * @param cloudEvent - * @return - */ - Collection extensionsOf(CloudEvent cloudEvent); -} diff --git a/api/src/main/java/io/cloudevents/fun/ExtensionMarshaller.java b/api/src/main/java/io/cloudevents/fun/ExtensionMarshaller.java deleted file mode 100644 index 13e2139d4..000000000 --- a/api/src/main/java/io/cloudevents/fun/ExtensionMarshaller.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Collection; -import java.util.Map; - -import io.cloudevents.extensions.ExtensionFormat; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface ExtensionMarshaller { - - /** - * Marshals a collections of {@link ExtensionFormat} into map - * @param extensions - * @return - */ - Map marshal(Collection extensions); - -} diff --git a/api/src/main/java/io/cloudevents/fun/ExtensionUmarshaller.java b/api/src/main/java/io/cloudevents/fun/ExtensionUmarshaller.java deleted file mode 100644 index 920656c10..000000000 --- a/api/src/main/java/io/cloudevents/fun/ExtensionUmarshaller.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; -import java.util.Optional; - -import io.cloudevents.extensions.ExtensionFormat; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface ExtensionUmarshaller { - - /** - * Unmarshals the map of extensions into {@link ExtensionFormat} - * @param extensions - * @return - */ - Optional unmarshal(Map extensions); - -} diff --git a/api/src/main/java/io/cloudevents/fun/FormatExtensionMapper.java b/api/src/main/java/io/cloudevents/fun/FormatExtensionMapper.java deleted file mode 100644 index fc345fe77..000000000 --- a/api/src/main/java/io/cloudevents/fun/FormatExtensionMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface FormatExtensionMapper { - - /** - * Maps the 'headers' of binary format into extensions - * @param headers - */ - Map map(Map headers); - -} diff --git a/api/src/main/java/io/cloudevents/fun/FormatHeaderMapper.java b/api/src/main/java/io/cloudevents/fun/FormatHeaderMapper.java deleted file mode 100644 index 400f54d4a..000000000 --- a/api/src/main/java/io/cloudevents/fun/FormatHeaderMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -/** - * - * @author fabiojose - * @param The header value type - * - */ -@FunctionalInterface -public interface FormatHeaderMapper { - - /** - * Maps the 'attributes' and 'extensions' of CloudEvent envelop to - * 'headers' of binary format - * @param attributes - * @return - */ - Map map(Map attributes, - Map extensions); - -} diff --git a/api/src/main/java/io/cloudevents/fun/WireBuilder.java b/api/src/main/java/io/cloudevents/fun/WireBuilder.java deleted file mode 100644 index 2a993f2a0..000000000 --- a/api/src/main/java/io/cloudevents/fun/WireBuilder.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.fun; - -import java.util.Map; - -import io.cloudevents.format.Wire; - -/** - * - * @author fabiojose - * - */ -@FunctionalInterface -public interface WireBuilder { - - /** - * Builds a wire format - * @param payload - * @param headers - * @return - */ - Wire build(P payload, Map headers); - -} diff --git a/api/src/main/java/io/cloudevents/impl/AttributesInternal.java b/api/src/main/java/io/cloudevents/impl/AttributesInternal.java new file mode 100644 index 000000000..33d4913ef --- /dev/null +++ b/api/src/main/java/io/cloudevents/impl/AttributesInternal.java @@ -0,0 +1,6 @@ +package io.cloudevents.impl; + +import io.cloudevents.Attributes; +import io.cloudevents.message.BinaryMessageAttributes; + +public interface AttributesInternal extends Attributes, BinaryMessageAttributes { } diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index 25fb0f677..91910d2e6 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -7,9 +7,11 @@ import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.DataConversionException; +import io.cloudevents.format.EventFormat; import io.cloudevents.format.json.CloudEventDeserializer; import io.cloudevents.format.json.CloudEventSerializer; import io.cloudevents.json.Json; +import io.cloudevents.message.*; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -17,15 +19,15 @@ @JsonSerialize(using = CloudEventSerializer.class) @JsonDeserialize(using = CloudEventDeserializer.class) -public final class CloudEventImpl implements CloudEvent { +public final class CloudEventImpl implements CloudEvent, BinaryMessage { - private final Attributes attributes; + private final AttributesInternal attributes; private final Object data; private final Map extensions; - public CloudEventImpl(Attributes attributes, Object data, Map extensions) { + protected CloudEventImpl(Attributes attributes, Object data, Map extensions) { Objects.requireNonNull(attributes); - this.attributes = attributes; + this.attributes = (AttributesInternal) attributes; this.data = data; this.extensions = extensions != null ? extensions : new HashMap<>(); } @@ -130,4 +132,48 @@ public CloudEvent toV1() { protected Object getRawData() { return data; } + + // Message impl + + public BinaryMessage asBinaryMessage() { + return this; + } + + public StructuredMessage asStructuredMessage(EventFormat format) { + CloudEvent ev = this; + // TODO This sucks, will improve later + return new StructuredMessage() { + @Override + public T visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException { + return visitor.setEvent(format, format.serializeToBytes(ev)); + } + }; + } + + @Override + public , V> V visit(BinaryMessageVisitorFactory visitorFactory) throws MessageVisitException, IllegalStateException { + BinaryMessageVisitor visitor = visitorFactory.apply(this.attributes.getSpecVersion()); + this.attributes.visit(visitor); + + // TODO to be improved + for (Map.Entry entry : this.extensions.entrySet()) { + if (entry.getValue() instanceof String) { + visitor.setExtension(entry.getKey(), (String) entry.getValue()); + } else if (entry.getValue() instanceof Number) { + visitor.setExtension(entry.getKey(), (Number) entry.getValue()); + } else if (entry.getValue() instanceof Boolean) { + visitor.setExtension(entry.getKey(), (Boolean) entry.getValue()); + } else { + // This should never happen because we build that map only through our builders + throw new IllegalStateException("Illegal value inside extensions map: " + entry); + } + } + + // TODO to be improved to remove the allocation of useless optional + if (this.data != null) { + visitor.setBody(this.getDataAsBytes().get()); + } + + return visitor.end(); + } } diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitException.java b/api/src/main/java/io/cloudevents/message/MessageVisitException.java index 53cfb3e27..c0650de01 100644 --- a/api/src/main/java/io/cloudevents/message/MessageVisitException.java +++ b/api/src/main/java/io/cloudevents/message/MessageVisitException.java @@ -3,8 +3,11 @@ public class MessageVisitException extends RuntimeException { public enum MessageVisitExceptionKind { - INVALID_ATTRIBUTE_TYPE, INVALID_SPEC_VERSION, + INVALID_ATTRIBUTE_NAME, + INVALID_ATTRIBUTE_TYPE, + INVALID_ATTRIBUTE_VALUE, + INVALID_EXTENSION_TYPE, } private MessageVisitExceptionKind kind; @@ -23,19 +26,39 @@ public MessageVisitExceptionKind getKind() { return kind; } - public static MessageVisitException newInvalidAttributeType(String attributeName, Class clazz) { + public static MessageVisitException newInvalidSpecVersion(String specVersion) { return new MessageVisitException( MessageVisitExceptionKind.INVALID_ATTRIBUTE_TYPE, - "Invalid attribute type for " + attributeName + ": " + clazz.getCanonicalName() + "Invalid specversion: " + specVersion ); } - public static MessageVisitException newInvalidSpecVersion(String specVersion) { + public static MessageVisitException newInvalidAttributeName(String attributeName) { + return new MessageVisitException( + MessageVisitExceptionKind.INVALID_ATTRIBUTE_NAME, + "Invalid attribute: " + attributeName + ); + } + + public static MessageVisitException newInvalidAttributeType(String attributeName, Class clazz) { return new MessageVisitException( MessageVisitExceptionKind.INVALID_ATTRIBUTE_TYPE, - "Invalid specversion: " + specVersion + "Invalid attribute type for " + attributeName + ": " + clazz.getCanonicalName() ); } + public static MessageVisitException newInvalidAttributeValue(String attributeName, Object value, Throwable cause) { + return new MessageVisitException( + MessageVisitExceptionKind.INVALID_ATTRIBUTE_VALUE, + "Invalid attribute value for " + attributeName + ": " + value, + cause + ); + } + public static MessageVisitException newInvalidExtensionType(String extensionName, Class clazz) { + return new MessageVisitException( + MessageVisitExceptionKind.INVALID_EXTENSION_TYPE, + "Invalid extension type for " + extensionName + ": " + clazz.getCanonicalName() + ); + } } diff --git a/api/src/main/java/io/cloudevents/types/Time.java b/api/src/main/java/io/cloudevents/types/Time.java index e00f965af..734ac01ea 100644 --- a/api/src/main/java/io/cloudevents/types/Time.java +++ b/api/src/main/java/io/cloudevents/types/Time.java @@ -1,7 +1,13 @@ package io.cloudevents.types; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; public final class Time { public static final DateTimeFormatter RFC3339_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); + + public static ZonedDateTime parseTime(String time) throws DateTimeParseException { + return ZonedDateTime.parse(time, RFC3339_DATE_FORMAT); + } } diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index 364a0c82e..7319beca6 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -17,7 +17,7 @@ import io.cloudevents.Attributes; import io.cloudevents.SpecVersion; -import io.cloudevents.message.BinaryMessageAttributes; +import io.cloudevents.impl.AttributesInternal; import io.cloudevents.message.BinaryMessageAttributesVisitor; import io.cloudevents.message.MessageVisitException; @@ -32,7 +32,7 @@ * @author slinkydeveloper * */ -public class AttributesImpl implements Attributes, BinaryMessageAttributes { +public final class AttributesImpl implements AttributesInternal { private final String id; private final URI source; diff --git a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java index 310e0a20f..f84047644 100644 --- a/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v03/CloudEventBuilder.java @@ -19,9 +19,12 @@ import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; import io.cloudevents.message.MessageVisitException; +import io.cloudevents.types.Time; import java.net.URI; +import java.net.URISyntaxException; import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; /** @@ -112,16 +115,63 @@ protected AttributesImpl buildAttributes() { @Override public void setAttribute(String name, String value) throws MessageVisitException { - + switch (name) { + case "id": + withId(value); + return; + case "source": + try { + withSource(new URI(value)); + } catch (URISyntaxException e) { + throw MessageVisitException.newInvalidAttributeValue("source", value, e); + } + return; + case "type": + withType(value); + return; + case "datacontenttype": + withDataContentType(value); + return; + case "schemaurl": + try { + withSchemaUrl(new URI(value)); + } catch (URISyntaxException e) { + throw MessageVisitException.newInvalidAttributeValue("schemaurl", value, e); + } + return; + case "subject": + withSubject(value); + return; + case "time": + try { + withTime(Time.parseTime(value)); + } catch (DateTimeParseException e) { + throw MessageVisitException.newInvalidAttributeValue("time", value, e); + } + return; + } + throw MessageVisitException.newInvalidAttributeName(name); } @Override public void setAttribute(String name, URI value) throws MessageVisitException { - + switch (name) { + case "source": + withSource(value); + return; + case "schemaurl": + withDataSchema(value); + return; + } + throw MessageVisitException.newInvalidAttributeType(name, URI.class); } @Override public void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { - + if ("time".equals(name)) { + withTime(value); + return; + } + throw MessageVisitException.newInvalidAttributeType(name, ZonedDateTime.class); } } diff --git a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java index f14414223..c6512beff 100644 --- a/api/src/main/java/io/cloudevents/v03/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v03/ContextAttributes.java @@ -44,4 +44,9 @@ public enum ContextAttributes { public static ContextAttributes parse(String value) { return ContextAttributes.valueOf(value.toUpperCase()); } + + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 2f16cf678..684335ff4 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -17,7 +17,7 @@ import io.cloudevents.Attributes; import io.cloudevents.SpecVersion; -import io.cloudevents.message.BinaryMessageAttributes; +import io.cloudevents.impl.AttributesInternal; import io.cloudevents.message.BinaryMessageAttributesVisitor; import io.cloudevents.message.MessageVisitException; @@ -31,7 +31,7 @@ * @author slinkydeveloper * @version 1.0 */ -public class AttributesImpl implements Attributes, BinaryMessageAttributes { +public final class AttributesImpl implements AttributesInternal { private final String id; private final URI source; diff --git a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java index 70a6f1f5a..00f54c22a 100644 --- a/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/v1/CloudEventBuilder.java @@ -4,9 +4,12 @@ import io.cloudevents.CloudEvent; import io.cloudevents.impl.BaseCloudEventBuilder; import io.cloudevents.message.MessageVisitException; +import io.cloudevents.types.Time; import java.net.URI; +import java.net.URISyntaxException; import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; /** * @@ -90,16 +93,63 @@ protected AttributesImpl buildAttributes() { @Override public void setAttribute(String name, String value) throws MessageVisitException { - + switch (name) { + case "id": + withId(value); + return; + case "source": + try { + withSource(new URI(value)); + } catch (URISyntaxException e) { + throw MessageVisitException.newInvalidAttributeValue("source", value, e); + } + return; + case "type": + withType(value); + return; + case "datacontenttype": + withDataContentType(value); + return; + case "dataschema": + try { + withDataSchema(new URI(value)); + } catch (URISyntaxException e) { + throw MessageVisitException.newInvalidAttributeValue("dataschema", value, e); + } + return; + case "subject": + withSubject(value); + return; + case "time": + try { + withTime(Time.parseTime(value)); + } catch (DateTimeParseException e) { + throw MessageVisitException.newInvalidAttributeValue("time", value, e); + } + return; + } + throw MessageVisitException.newInvalidAttributeName(name); } @Override public void setAttribute(String name, URI value) throws MessageVisitException { - + switch (name) { + case "source": + withSource(value); + return; + case "dataschema": + withDataSchema(value); + return; + } + throw MessageVisitException.newInvalidAttributeType(name, URI.class); } @Override public void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { - + if ("time".equals(name)) { + withTime(value); + return; + } + throw MessageVisitException.newInvalidAttributeType(name, ZonedDateTime.class); } } diff --git a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java index 530c66f21..9ec92d9a7 100644 --- a/api/src/main/java/io/cloudevents/v1/ContextAttributes.java +++ b/api/src/main/java/io/cloudevents/v1/ContextAttributes.java @@ -35,11 +35,15 @@ public enum ContextAttributes { TIME; public static final List VALUES = Arrays.stream(ContextAttributes.values()) - .map(Enum::name) - .map(String::toLowerCase) + .map(ContextAttributes::toString) .collect(Collectors.toList()); public static ContextAttributes parse(String value) { return ContextAttributes.valueOf(value.toUpperCase()); } + + @Override + public String toString() { + return name().toLowerCase(); + } } From c7b9f3dab50809f098ce2538b3faf547fefebfaa Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 15:52:49 +0200 Subject: [PATCH 10/21] Compiles! Signed-off-by: Francesco Guardiani --- .../io/cloudevents/impl/CloudEventImpl.java | 27 +- .../main/java/io/cloudevents/json/Json.java | 282 --------------- .../message/BinaryMessageVisitorFactory.java | 6 +- .../java/io/cloudevents/message/Message.java | 8 + .../cloudevents/message/MessageVisitor.java | 3 + .../io/cloudevents/v03/AttributesImpl.java | 103 ++---- .../io/cloudevents/v1/AttributesImpl.java | 91 +---- .../format/StructuredMarshallerTest.java | 159 --------- .../format/StructuredUnmarshallerTest.java | 151 -------- .../java/io/cloudevents/format/WireTest.java | 79 ----- .../http/HttpTransportAttributesTest.java | 106 ------ .../cloudevents/impl/CloudEventImplTest.java | 4 + .../java/io/cloudevents/json/JsonTest.java | 76 ---- .../message/EventMessageRoundtripTest.java | 26 ++ .../cloudevents/mock/MockBinaryMessage.java | 122 +++++++ .../test/java/io/cloudevents/test/Data.java | 55 +++ .../java/io/cloudevents/v03/AccessorTest.java | 137 ------- .../v03/CloudEventBuilderTest.java | 236 +------------ .../v03/CloudEventJacksonTest.java | 283 --------------- .../v03/http/AttributeMapperTest.java | 114 ------ .../v03/http/ExtensionMapperTest.java | 105 ------ .../v03/http/HTTPBinaryMarshallerTest.java | 131 ------- .../v03/http/HTTPBinaryUnmarshallerTest.java | 114 ------ .../http/HTTPStructuredMarshallerTest.java | 149 -------- .../v03/http/HTTPStructuredUnmarshaller.java | 157 -------- .../v03/http/HeaderMapperTest.java | 109 ------ .../java/io/cloudevents/v1/AccessorTest.java | 136 ------- .../cloudevents/v1/CloudEventBuilderTest.java | 214 +---------- .../cloudevents/v1/CloudEventJacksonTest.java | 334 ------------------ .../v1/http/AttributeMapperTest.java | 114 ------ .../v1/http/ExtensionMapperTest.java | 104 ------ .../v1/http/HTTPBinaryMarshallerTest.java | 164 --------- .../v1/http/HTTPBinaryUnmarshallerTest.java | 113 ------ .../v1/http/HTTPStructuredMarshallerTest.java | 98 ----- .../http/HTTPStructuredUnmarshallerTest.java | 204 ----------- .../cloudevents/v1/http/HeaderMapperTest.java | 111 ------ .../cloudevents/validation/MockValidator.java | 42 --- .../cloudevents/v1/kafka/Unmarshallers.java | 74 ++-- .../kafka/CloudEventsKafkaHeadersTest.java | 2 +- 39 files changed, 338 insertions(+), 4205 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/json/Json.java create mode 100644 api/src/main/java/io/cloudevents/message/MessageVisitor.java delete mode 100644 api/src/test/java/io/cloudevents/format/StructuredMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/format/StructuredUnmarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/format/WireTest.java delete mode 100644 api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java create mode 100644 api/src/test/java/io/cloudevents/impl/CloudEventImplTest.java delete mode 100644 api/src/test/java/io/cloudevents/json/JsonTest.java create mode 100644 api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java create mode 100644 api/src/test/java/io/cloudevents/mock/MockBinaryMessage.java create mode 100644 api/src/test/java/io/cloudevents/test/Data.java delete mode 100644 api/src/test/java/io/cloudevents/v03/AccessorTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/AttributeMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/ExtensionMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/HTTPBinaryMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/HTTPStructuredMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java delete mode 100644 api/src/test/java/io/cloudevents/v03/http/HeaderMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/AccessorTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/AttributeMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/ExtensionMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/HTTPBinaryMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java delete mode 100644 api/src/test/java/io/cloudevents/v1/http/HeaderMapperTest.java delete mode 100644 api/src/test/java/io/cloudevents/validation/MockValidator.java diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index 91910d2e6..a391386a9 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -10,7 +10,7 @@ import io.cloudevents.format.EventFormat; import io.cloudevents.format.json.CloudEventDeserializer; import io.cloudevents.format.json.CloudEventSerializer; -import io.cloudevents.json.Json; +import io.cloudevents.format.json.JsonFormat; import io.cloudevents.message.*; import java.io.IOException; @@ -49,7 +49,7 @@ public Optional getDataAsString() { if (data instanceof JsonNode) { JsonNode d = (JsonNode) this.data; try { - return Optional.of(Json.MAPPER.writeValueAsString(data)); + return Optional.of(JsonFormat.MAPPER.writeValueAsString(data)); } catch (JsonProcessingException e) { throw new DataConversionException("JsonNode", "String", e); } @@ -71,7 +71,7 @@ public Optional getDataAsBytes() { if (data instanceof JsonNode) { JsonNode d = (JsonNode) this.data; try { - return Optional.of(Json.MAPPER.writeValueAsBytes(data)); + return Optional.of(JsonFormat.MAPPER.writeValueAsBytes(data)); } catch (JsonProcessingException e) { throw new DataConversionException("JsonNode", "byte[]", e); } @@ -86,14 +86,14 @@ public Optional getDataAsJson() { if (data != null) { if (data instanceof String) { try { - return Optional.of(Json.MAPPER.readTree((String)data)); + return Optional.of(JsonFormat.MAPPER.readTree((String)data)); } catch (IOException e) { throw new DataConversionException("String", "JsonNode", e); } } if (data instanceof byte[]) { try { - return Optional.of(Json.MAPPER.readTree((byte[]) data)); + return Optional.of(JsonFormat.MAPPER.readTree((byte[]) data)); } catch (IOException e) { throw new DataConversionException("[]byte", "JsonNode", e); } @@ -152,7 +152,7 @@ public T visit(StructuredMessageVisitor visitor) throws MessageVisitExcep @Override public , V> V visit(BinaryMessageVisitorFactory visitorFactory) throws MessageVisitException, IllegalStateException { - BinaryMessageVisitor visitor = visitorFactory.apply(this.attributes.getSpecVersion()); + BinaryMessageVisitor visitor = visitorFactory.createBinaryMessageVisitor(this.attributes.getSpecVersion()); this.attributes.visit(visitor); // TODO to be improved @@ -176,4 +176,19 @@ public , V> V visit(BinaryMessageVisitorFactor return visitor.end(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CloudEventImpl that = (CloudEventImpl) o; + return Objects.equals(attributes, that.attributes) && + Objects.equals(data, that.data) && + Objects.equals(extensions, that.extensions); + } + + @Override + public int hashCode() { + return Objects.hash(attributes, data, extensions); + } } diff --git a/api/src/main/java/io/cloudevents/json/Json.java b/api/src/main/java/io/cloudevents/json/Json.java deleted file mode 100644 index 48fdc66a2..000000000 --- a/api/src/main/java/io/cloudevents/json/Json.java +++ /dev/null @@ -1,282 +0,0 @@ -/** - * Copyright 2018 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.json; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.type.TypeFactory; -import io.cloudevents.Attributes; -import io.cloudevents.CloudEvent; -import io.cloudevents.format.json.ZonedDateTimeDeserializer; -import io.cloudevents.format.json.ZonedDateTimeSerializer; -import io.cloudevents.fun.DataMarshaller; -import io.cloudevents.fun.DataUnmarshaller; - -import java.io.InputStream; -import java.time.ZonedDateTime; -import java.util.Map; - -public final class Json { - - public static final ObjectMapper MAPPER = new ObjectMapper(); - - static { - // add ZonedDateTime ser/de - final SimpleModule module = new SimpleModule("Custom ZonedDateTime"); - module.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer()); - module.addDeserializer(ZonedDateTime.class, new ZonedDateTimeDeserializer()); - MAPPER.registerModule(module); - } - - public static String encode(final CloudEvent event) throws IllegalStateException { - - } - - public static byte[] encodeToBinary(final CloudEvent event) throws IllegalStateException { - - } - - public static CloudEvent decode(final byte[] binary) { - - } - - public static CloudEvent decode(final String string) { - - } - - // TODO remove all the stuff below - - /** - * Encode a POJO to JSON using the underlying Jackson mapper. - * - * @param obj a POJO - * @return a String containing the JSON representation of the given POJO. - * @throws IllegalStateException if a property cannot be encoded. - */ - public static String encode(final Object obj) throws IllegalStateException { - try { - return MAPPER.writeValueAsString(obj); - } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); - } - } - - /** - * Encode a POJO to JSON using the underlying Jackson mapper. - * - * @param obj a POJO - * @return a byte array containing the JSON representation of the given POJO. - * @throws IllegalStateException if a property cannot be encoded. - */ - public static byte[] binaryEncode(final Object obj) throws IllegalStateException { - try { - return MAPPER.writeValueAsBytes(obj); - } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage()); - } - } - - public static T fromInputStream(final InputStream inputStream, - Class clazz) { - try { - return MAPPER.readValue(inputStream, clazz); - } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " - + e.getMessage()); - } - } - - public static T fromInputStream(final InputStream inputStream, - final TypeReference type) { - try { - return MAPPER.readValue(inputStream, type); - } catch (Exception e) { - throw new IllegalStateException("Failed to encode as JSON: " - + e.getMessage()); - } - } - - /** - * Decode a given JSON string to a POJO of the given class type. - * - * @param str the JSON string. - * @param clazz the class to map to. - * @param the generic type. - * @return an instance of T or {@code null} when {@code str} is an empty string or {@code null} - * @throws IllegalStateException when there is a parsing or invalid mapping. - */ - protected static T decodeValue(final String str, final Class clazz) throws IllegalStateException { - - if(null!= str && !"".equals(str.trim())) { - try { - return MAPPER.readValue(str.trim(), clazz); - } catch (Exception e) { - throw new IllegalStateException("Failed to decode: " + e.getMessage()); - } - } - - return null; - } - - protected static T binaryDecodeValue(byte[] payload, final Class clazz) { - if(null!= payload) { - try { - return MAPPER.readValue(payload, clazz); - } catch (Exception e) { - throw new IllegalStateException("Failed to decode: " + e.getMessage()); - } - } - return null; - } - - /** - * Decode a given JSON string to a POJO of the given type. - * - * @param str the JSON string. - * @param type the type to map to. - * @param the generic type. - * @return an instance of T or {@code null} when {@code str} is an empty string or {@code null} - * @throws IllegalStateException when there is a parsing or invalid mapping. - */ - public static T decodeValue(final String str, final TypeReference type) throws IllegalStateException { - if(null!= str && !"".equals(str.trim())) { - try { - return MAPPER.readValue(str.trim(), type); - } catch (Exception e) { - throw new IllegalStateException("Failed to decode: " + e.getMessage(), e); - } - } - return null; - } - - /** - * Example of use: - *

-     * String someJson = "...";
-     * Class clazz = Much.class;
-     *
-     * Json.decodeValue(someJson, CloudEventImpl.class, clazz);
-     * 
- * @param str The JSON String to parse - * @param parametrized Actual full type - * @param parameterClasses Type parameters to apply - * @param the generic type. - * @return An instance of T or {@code null} when {@code str} is an empty string or {@code null} - * @see ObjectMapper#getTypeFactory - * @see TypeFactory#constructParametricType(Class, Class...) - */ - public static T decodeValue(final String str, Class parametrized, - Class...parameterClasses) { - if(null!= str && !"".equals(str.trim())) { - try { - JavaType type = - MAPPER.getTypeFactory() - .constructParametricType(parametrized, - parameterClasses); - - return MAPPER.readValue(str.trim(), type); - } catch (Exception e) { - throw new IllegalStateException("Failed to decode: " + e.getMessage(), e); - } - } - return null; - } - - /** - * Example of use: - *
-     * String someJson = "...";
-     * Class clazz = Much.class;
-     *
-     * Json.decodeValue(someJson, CloudEventImpl.class, clazz);
-     * 
- * @param json The JSON byte array to parse - * @param parametrized Actual full type - * @param parameterClasses Type parameters to apply - * @param the generic type. - * @return An instance of T or {@code null} when {@code str} is an empty string or {@code null} - * @see ObjectMapper#getTypeFactory - * @see TypeFactory#constructParametricType(Class, Class...) - */ - public static T binaryDecodeValue(final byte[] json, Class parametrized, - Class...parameterClasses) { - if(null!= json) { - try { - JavaType type = - MAPPER.getTypeFactory() - .constructParametricType(parametrized, - parameterClasses); - - return MAPPER.readValue(json, type); - } catch (Exception e) { - throw new IllegalStateException("Failed to decode: " + e.getMessage(), e); - } - } - return null; - } - - /** - * Creates a JSON Data Unmarshaller - * @param The 'data' type - * @param
The attributes type - * @param type The type of 'data' - * @return A new instance of {@link DataUnmarshaller} - */ - public static DataUnmarshaller - umarshaller(Class type) { - return (payload, attributes) -> Json.decodeValue(payload, type); - } - - /** - * Unmarshals a byte array into T type - * @param The 'data' type - * @param The attributes type - * @return The data objects - */ - public static DataUnmarshaller - binaryUmarshaller(Class type) { - return (payload, attributes) -> Json.binaryDecodeValue(payload, type); - } - - /** - * Creates a JSON Data Marshaller that produces a {@link String} - * @param The 'data' type - * @param The type of headers value - * @return A new instance of {@link DataMarshaller} - */ - public static DataMarshaller marshaller() { - return (data, headers) -> Json.encode(data); - } - - /** - * Marshalls the 'data' value as JSON, producing a byte array - * @param The 'data' type - * @param The type of headers value - * @param data The 'data' value - * @param headers The headers - * @return A byte array with 'data' value encoded JSON - */ - public static byte[] binaryMarshal(T data, - Map headers) { - return Json.binaryEncode(data); - } - - private Json() { - // no-op - } -} diff --git a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java index 309999638..f4fb81ea3 100644 --- a/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java +++ b/api/src/main/java/io/cloudevents/message/BinaryMessageVisitorFactory.java @@ -2,7 +2,7 @@ import io.cloudevents.SpecVersion; -import java.util.function.Function; - @FunctionalInterface -public interface BinaryMessageVisitorFactory, V> extends Function { } +public interface BinaryMessageVisitorFactory, V> { + T createBinaryMessageVisitor(SpecVersion version); +} diff --git a/api/src/main/java/io/cloudevents/message/Message.java b/api/src/main/java/io/cloudevents/message/Message.java index 99d29e54b..56ecf0584 100644 --- a/api/src/main/java/io/cloudevents/message/Message.java +++ b/api/src/main/java/io/cloudevents/message/Message.java @@ -6,6 +6,14 @@ public interface Message extends StructuredMessage, BinaryMessage { Encoding getEncoding(); + default , R> R visit(MessageVisitor visitor) throws MessageVisitException, IllegalStateException { + switch (getEncoding()) { + case BINARY: return this.visit((BinaryMessageVisitorFactory) visitor); + case STRUCTURED: return this.visit((StructuredMessageVisitor)visitor); + default: throw Encoding.UNKNOWN_ENCODING_EXCEPTION; + } + } + default CloudEvent toEvent() throws MessageVisitException, IllegalStateException { switch (getEncoding()) { case BINARY: return ((BinaryMessage)this).toEvent(); diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitor.java b/api/src/main/java/io/cloudevents/message/MessageVisitor.java new file mode 100644 index 000000000..9d6e9c865 --- /dev/null +++ b/api/src/main/java/io/cloudevents/message/MessageVisitor.java @@ -0,0 +1,3 @@ +package io.cloudevents.message; + +public interface MessageVisitor, R> extends BinaryMessageVisitorFactory, StructuredMessageVisitor { } diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index 7319beca6..57e18c7cc 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -23,6 +23,7 @@ import java.net.URI; import java.time.ZonedDateTime; +import java.util.Objects; import java.util.Optional; /** @@ -74,6 +75,7 @@ public String getType() { public Optional getDataContentType() { return Optional.ofNullable(datacontenttype); } + public Optional getDataSchema() { return getSchemaUrl(); } @@ -156,85 +158,24 @@ public String toString() { + ", datacontenttype=" + datacontenttype + ", subject=" + subject + "]"; } -// -// /** -// * Used by the Jackson framework to unmarshall. -// */ -// @JsonCreator -// public static AttributesImpl build( -// @JsonProperty("id") String id, -// @JsonProperty("source") URI source, -// @JsonProperty("type") String type, -// @JsonProperty("time") ZonedDateTime time, -// @JsonProperty("schemaurl") URI schemaurl, -// @JsonProperty("datacontenttype") String datacontenttype, -// @JsonProperty("subject") String subject) { -// -// return new AttributesImpl(id, source, type, time, -// schemaurl, datacontenttype, subject); -// } -// -// /** -// * Creates the marshaller instance to marshall {@link AttributesImpl} as -// * a {@link Map} of strings -// */ -// public static Map marshal(AttributesImpl attributes) { -// Objects.requireNonNull(attributes); -// -// Map result = new HashMap<>(); -// -// result.put(ContextAttributes.TYPE.name(), -// attributes.getType()); -// result.put(ContextAttributes.SPECVERSION.name(), -// attributes.getSpecVersion()); -// result.put(ContextAttributes.SOURCE.name(), -// attributes.getSource().toString()); -// result.put(ContextAttributes.ID.name(), -// attributes.getId()); -// -// attributes.getTime().ifPresent((value) -> result.put(ContextAttributes.TIME.name(), -// value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); -// attributes.getSchemaurl().ifPresent((schema) -> result.put(ContextAttributes.SCHEMAURL.name(), -// schema.toString())); -// attributes.getDatacontenttype().ifPresent((ct) -> result.put(ContextAttributes.DATACONTENTTYPE.name(), ct)); -// attributes.getDatacontentencoding().ifPresent(dce -> result.put(ContextAttributes.DATACONTENTENCODING.name(), dce)); -// attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); -// -// return result; -// } -// -// /** -// * The attribute unmarshaller for the binary format, that receives a -// * {@code Map} with attributes names as String and value as String. -// */ -// public static AttributesImpl unmarshal(Map attributes) { -// String type = attributes.get(ContextAttributes.TYPE.name()); -// ZonedDateTime time = -// Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) -// .map((t) -> ZonedDateTime.parse(t, -// ISO_ZONED_DATE_TIME)) -// .orElse(null); -// -// String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); -// URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); -// -// URI schemaurl = -// Optional.ofNullable(attributes.get(ContextAttributes.SCHEMAURL.name())) -// .map(URI::create) -// .orElse(null); -// -// String id = attributes.get(ContextAttributes.ID.name()); -// -// String datacontenttype = -// attributes.get(ContextAttributes.DATACONTENTTYPE.name()); -// -// String datacontentencoding = -// attributes.get(ContextAttributes.DATACONTENTENCODING.name()); -// -// String subject = attributes.get(ContextAttributes.SUBJECT.name()); -// -// return AttributesImpl.build(id, source, specversion, type, -// time, schemaurl, datacontentencoding, -// datacontenttype, subject); -// } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AttributesImpl that = (AttributesImpl) o; + return Objects.equals(id, that.id) && + Objects.equals(source, that.source) && + Objects.equals(type, that.type) && + Objects.equals(datacontenttype, that.datacontenttype) && + Objects.equals(schemaurl, that.schemaurl) && + Objects.equals(subject, that.subject) && + Objects.equals(time, that.time); + } + + @Override + public int hashCode() { + return Objects.hash(id, source, type, datacontenttype, schemaurl, subject, time); + } + } diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index 684335ff4..def3bba3b 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -23,6 +23,7 @@ import java.net.URI; import java.time.ZonedDateTime; +import java.util.Objects; import java.util.Optional; /** @@ -155,76 +156,22 @@ public String toString() { + ", time=" + time + "]"; } -// /** -// * Used by the Jackson framework to unmarshall. -// */ -// @JsonCreator -// public static AttributesImpl build( -// @JsonProperty("id") String id, -// @JsonProperty("source") URI source, -// @JsonProperty("type") String type, -// @JsonProperty("datacontenttype") String datacontenttype, -// @JsonProperty("dataschema") URI dataschema, -// @JsonProperty("subject") String subject, -// @JsonProperty("time") ZonedDateTime time) { -// -// return new AttributesImpl(id, source, type, -// datacontenttype, dataschema, subject, time); -// } -// -// /** -// * Creates the marshaller instance to marshall {@link AttributesImpl} as -// * a {@link Map} of strings -// */ -// public static Map marshal(AttributesImpl attributes) { -// Objects.requireNonNull(attributes); -// Map result = new HashMap<>(); -// -// result.put(ContextAttributes.ID.name(), -// attributes.getId()); -// result.put(ContextAttributes.SOURCE.name(), -// attributes.getSource().toString()); -// result.put(ContextAttributes.TYPE.name(), -// attributes.getType()); -// -// attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.DATACONTENTTYPE.name(), dct)); -// attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.DATASCHEMA.name(), -// dataschema.toString())); -// attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.SUBJECT.name(), subject)); -// attributes.getTime().ifPresent(time -> result.put(ContextAttributes.TIME.name(), -// time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); -// -// return result; -// } -// -// /** -// * The attribute unmarshaller for the binary format, that receives a -// * {@code Map} with attributes names as String and value as String. -// */ -// public static AttributesImpl unmarshal(Map attributes) { -// String type = attributes.get(ContextAttributes.TYPE.name()); -// ZonedDateTime time = -// Optional.ofNullable(attributes.get(ContextAttributes.TIME.name())) -// .map((t) -> ZonedDateTime.parse(t, -// ISO_ZONED_DATE_TIME)) -// .orElse(null); -// -// String specversion = attributes.get(ContextAttributes.SPECVERSION.name()); -// URI source = URI.create(attributes.get(ContextAttributes.SOURCE.name())); -// -// URI dataschema = -// Optional.ofNullable(attributes.get(ContextAttributes.DATASCHEMA.name())) -// .map(URI::create) -// .orElse(null); -// -// String id = attributes.get(ContextAttributes.ID.name()); -// -// String datacontenttype = -// attributes.get(ContextAttributes.DATACONTENTTYPE.name()); -// -// String subject = attributes.get(ContextAttributes.SUBJECT.name()); -// -// return AttributesImpl.build(id, source, type, -// datacontenttype, dataschema, subject, time); -// } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AttributesImpl that = (AttributesImpl) o; + return Objects.equals(id, that.id) && + Objects.equals(source, that.source) && + Objects.equals(type, that.type) && + Objects.equals(datacontenttype, that.datacontenttype) && + Objects.equals(dataschema, that.dataschema) && + Objects.equals(subject, that.subject) && + Objects.equals(time, that.time); + } + + @Override + public int hashCode() { + return Objects.hash(id, source, type, datacontenttype, dataschema, subject, time); + } } diff --git a/api/src/test/java/io/cloudevents/format/StructuredMarshallerTest.java b/api/src/test/java/io/cloudevents/format/StructuredMarshallerTest.java deleted file mode 100644 index 45395d6ee..000000000 --- a/api/src/test/java/io/cloudevents/format/StructuredMarshallerTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.Attributes; -import io.cloudevents.json.types.Much; - -/** - * - * @author fabiojose - * - */ -public class StructuredMarshallerTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void should_throw_on_null_envelope_mime_header() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - StructuredMarshaller.builder() - .mime(null, ""); - } - - @Test - public void should_throw_on_null_envelope_mime_value() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - StructuredMarshaller.builder() - .mime("", null); - } - - @Test - public void should_be_ok_on_the_first_step() { - - // act - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json"); - } - - @Test - public void should_throw_on_null_marshaller_step() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map(null); - } - - @Test - public void should_throw_on_null_event() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .skip() - .withEvent(null); - } - - @Test - public void should_be_ok_on_the_third_step() { - // act - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .skip() - .withEvent(() -> null); - } - - @Test - public void should_throw_on_null_extension_accessor() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map(null); - } - - @Test - public void should_ok_on_the_extension_acessor() { - // act - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map((event) -> null); - } - - @Test - public void should_throw_on_null_extension_marshaller() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map((event) -> null) - .map(null); - } - - @Test - public void should_ok_on_extension_marshaller() { - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map((event) -> null) - .map((extensions) -> null); - } - - @Test - public void should_throw_on_null_header_mapper() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map((event) -> null) - .map((extensions) -> null) - .map(null); - } - - @Test - public void should_ok_on_header_mapper() { - StructuredMarshaller.builder() - .mime("Content-Type", "application/cloudevents+json") - .map((ce) -> null) - .map((event) -> null) - .map((extensions) -> null) - .map((attributes, extensions) -> null); - } -} diff --git a/api/src/test/java/io/cloudevents/format/StructuredUnmarshallerTest.java b/api/src/test/java/io/cloudevents/format/StructuredUnmarshallerTest.java deleted file mode 100644 index 156ca9898..000000000 --- a/api/src/test/java/io/cloudevents/format/StructuredUnmarshallerTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.Attributes; -import io.cloudevents.json.types.Much; - -/** - * - * @author fabiojose - * - */ -public class StructuredUnmarshallerTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void should_throw_on_null_extension_mapper() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredUnmarshaller.builder() - .map(null); - } - - @Test - public void should_ok_on_extension_mapper() { - StructuredUnmarshaller.builder() - .map((headers) -> { - - return null; - }); - } - - @Test - public void should_throw_on_null_extension_unmarshaller() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredUnmarshaller.builder() - .map((headers) -> { - - return null; - }) - .map(null); - } - - @Test - public void should_ok_on_extension_unmarshaller() { - StructuredUnmarshaller.builder() - .map((headers) -> { - - return null; - }) - .map((extensions) -> { - return null; - }); - } - - @Test - public void should_throw_on_null_envelope_unmarshaller() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredUnmarshaller.builder() - .map((headers) -> { - - return null; - }) - .map((extensions) -> { - return null; - }) - .next() - .map(null); - } - - @Test - public void should_ok_on_envelope_unmarshaller() { - StructuredUnmarshaller.builder() - .map((headers) -> null) - .map((extensions) -> null) - .next() - .map((payload, extensions) -> null); - } - - @Test - public void should_throw_on_null_headers() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredUnmarshaller.builder() - .map((headers) -> null) - .map((extensions) -> null) - .next() - .map((payload, extensions) -> null) - .withHeaders(null); - } - - @Test - public void should_ok_on_headers() { - StructuredUnmarshaller.builder() - .map((headers) -> null) - .map((extensions) -> null) - .next() - .map((payload, extensions) -> null) - .withHeaders(() -> null); - } - - @Test - public void should_throw_on_null_payload_supplier() { - // setup - expectedEx.expect(NullPointerException.class); - - StructuredUnmarshaller.builder() - .map((headers) -> null) - .map((extensions) -> null) - .next() - .map((payload, extensions) -> null) - .withHeaders(() -> null) - .withPayload(null); - } - - @Test - public void should_ok_on_payload_supplier() { - StructuredUnmarshaller.builder() - .map((headers) -> null) - .map((extensions) -> null) - .next() - .map((payload, extensions) -> null) - .withHeaders(() -> null) - .withPayload(() -> null); - } -} diff --git a/api/src/test/java/io/cloudevents/format/WireTest.java b/api/src/test/java/io/cloudevents/format/WireTest.java deleted file mode 100644 index 33dd942a7..000000000 --- a/api/src/test/java/io/cloudevents/format/WireTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.format; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * - * @author fabiojose - * - */ -public class WireTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void throws_error_when_null_headers() { - - // setup - expectedEx.expect(NullPointerException.class); - - new Wire("payload", null); - } - - @Test - public void throws_when_try_to_change_headers() { - // setup - expectedEx.expect(UnsupportedOperationException.class); - - Map headers = new HashMap<>(); - headers.put("contenttype", "application/json"); - - // act - Wire wire = new Wire<>("payload", headers); - - wire.getHeaders().put("my-header", "my-header-val"); - } - - @Test - public void should_ok_when_null_payload() { - Wire expected = - new Wire<>(null, new HashMap<>()); - - assertFalse(expected.getPayload().isPresent()); - } - - @Test - public void should_ok_when_payload_not_null() { - Wire actual = - new Wire<>("payload", new HashMap<>()); - - assertTrue(actual.getPayload().isPresent()); - assertEquals("payload", actual.getPayload().get()); - } - -} diff --git a/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java b/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java deleted file mode 100644 index fe5761615..000000000 --- a/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2018 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.http; - -import static org.junit.Assert.assertEquals; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -public class HttpTransportAttributesTest { - - @Test - public void testVersion02Headers() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - // act - Map attributes = io.cloudevents.v02.http.AttributeMapper.map(myHeaders); - - // assert - assertEquals("0x11", attributes.get("id")); - assertEquals("/source", attributes.get("source")); - assertEquals("0.2", attributes.get("specversion")); - assertEquals("br.my", attributes.get("type")); - assertEquals("2019-09-16T20:49:00Z", attributes.get("time")); - assertEquals("http://my.br", attributes.get("schemaurl")); - assertEquals("application/json", attributes.get("contenttype")); - } - - @Test - public void shoul_map_attributes_v02() { - // setup - Map attributes = new HashMap<>(); - attributes.put("id", "0x11"); - attributes.put("source", "/source"); - attributes.put("specversion", "0.2"); - attributes.put("type", "br.my"); - attributes.put("time", "2019-09-16T20:49:00Z"); - attributes.put("schemaurl", "http://my.br"); - attributes.put("contenttype", "application/json"); - - // act - Map headers = io.cloudevents.v02.http.HeaderMapper - .map(attributes, new HashMap()); - - // assert - assertEquals("0x11", headers.get("ce-id")); - assertEquals("/source", headers.get("ce-source")); - assertEquals("0.2", headers.get("ce-specversion")); - assertEquals("br.my", headers.get("ce-type")); - assertEquals("2019-09-16T20:49:00Z", headers.get("ce-time")); - assertEquals("http://my.br", headers.get("ce-schemaurl")); - assertEquals("application/json", headers.get("Content-Type")); - } - - @Test - public void should_map_headers_v03() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - myHeaders.put("ce-datacontentencoding", "base64"); - myHeaders.put("ce-subject", "the subject"); - - // act - Map attributes = io.cloudevents.v03.http.AttributeMapper.map(myHeaders); - - // assert - assertEquals("0x11", attributes.get("id")); - assertEquals("/source", attributes.get("source")); - assertEquals("0.2", attributes.get("specversion")); - assertEquals("br.my", attributes.get("type")); - assertEquals("2019-09-16T20:49:00Z", attributes.get("time")); - assertEquals("http://my.br", attributes.get("schemaurl")); - assertEquals("application/json", attributes.get("datacontenttype")); - assertEquals("base64", attributes.get("datacontentencoding")); - assertEquals("the subject", attributes.get("subject")); - } -} diff --git a/api/src/test/java/io/cloudevents/impl/CloudEventImplTest.java b/api/src/test/java/io/cloudevents/impl/CloudEventImplTest.java new file mode 100644 index 000000000..70fddcf47 --- /dev/null +++ b/api/src/test/java/io/cloudevents/impl/CloudEventImplTest.java @@ -0,0 +1,4 @@ +package io.cloudevents.impl; + +public class CloudEventImplTest { +} diff --git a/api/src/test/java/io/cloudevents/json/JsonTest.java b/api/src/test/java/io/cloudevents/json/JsonTest.java deleted file mode 100644 index e7ab82f7e..000000000 --- a/api/src/test/java/io/cloudevents/json/JsonTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.json; - -import static org.junit.Assert.assertNull; - -import java.util.Map; - -import org.junit.Test; - -import com.fasterxml.jackson.core.type.TypeReference; - -/** - * - * @author fabiojose - * - */ -public class JsonTest { - - @Test - public void should_result_null_on_decode_type_empty_string() { - // setup - String payload = ""; - - // act - Object actual = Json.decodeValue(payload, Map.class); - - // assert - assertNull(actual); - } - - @Test - public void should_result_null_on_decode_type_null_string() { - - // act - Object actual = Json.decodeValue(null, Map.class); - - // assert - assertNull(actual); - } - - @Test - public void should_result_null_on_decode_typereference_empty_string() { - // setup - String payload = ""; - - // act - Object actual = Json.decodeValue(payload, new TypeReference>() {}); - - // assert - assertNull(actual); - } - - @Test - public void should_result_null_on_decode_typereference_null_string() { - - // act - Object actual = Json.decodeValue(null, new TypeReference>() {}); - - // assert - assertNull(actual); - } -} diff --git a/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java b/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java new file mode 100644 index 000000000..6e574a10c --- /dev/null +++ b/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java @@ -0,0 +1,26 @@ +package io.cloudevents.message; + +import io.cloudevents.CloudEvent; +import io.cloudevents.format.json.JsonFormat; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EventMessageRoundtripTest { + + @ParameterizedTest() + @MethodSource("io.cloudevents.test.Data#allEvents") + void structuredToEvent(CloudEvent input) { + assertThat(input.asStructuredMessage(JsonFormat.getInstance()).toEvent()) + .isEqualTo(input); + } + + @ParameterizedTest() + @MethodSource("io.cloudevents.test.Data#allEvents") + void binaryToEvent(CloudEvent input) { + assertThat(input.asBinaryMessage().toEvent()) + .isEqualTo(input); + } + +} diff --git a/api/src/test/java/io/cloudevents/mock/MockBinaryMessage.java b/api/src/test/java/io/cloudevents/mock/MockBinaryMessage.java new file mode 100644 index 000000000..a784bf2ba --- /dev/null +++ b/api/src/test/java/io/cloudevents/mock/MockBinaryMessage.java @@ -0,0 +1,122 @@ +package io.cloudevents.mock; + +import io.cloudevents.SpecVersion; +import io.cloudevents.message.*; + +import java.net.URI; +import java.time.ZonedDateTime; +import java.util.HashMap; +import java.util.Map; + +public class MockBinaryMessage implements Message, BinaryMessageVisitorFactory, BinaryMessageVisitor { + + private SpecVersion version; + private Map attributes; + private byte[] data; + private Map extensions; + + public MockBinaryMessage(SpecVersion version, Map attributes, byte[] data, Map extensions) { + this.version = version; + this.attributes = attributes; + this.data = data; + this.extensions = extensions; + } + + public MockBinaryMessage() { + this.attributes = new HashMap<>(); + this.extensions = new HashMap<>(); + } + + @Override + public Encoding getEncoding() { + return Encoding.BINARY; + } + + @Override + public , V> V visit(BinaryMessageVisitorFactory visitorFactory) throws MessageVisitException, IllegalStateException { + if (version == null) { + throw new IllegalStateException("MockBinaryMessage is empty"); + } + + BinaryMessageVisitor visitor = visitorFactory.createBinaryMessageVisitor(version); + for (Map.Entry e: this.attributes.entrySet()) { + if (e.getValue() instanceof String) { + visitor.setAttribute(e.getKey(), (String) e.getValue()); + } else if (e.getValue() instanceof Number) { + visitor.setExtension(e.getKey(), (Number) e.getValue()); + } else if (e.getValue() instanceof Boolean) { + visitor.setExtension(e.getKey(), (Boolean) e.getValue()); + } else { + // This should never happen because we build that map only through our builders + throw new IllegalStateException("Illegal value inside extensions map: " + e); + } + } + + for (Map.Entry entry : this.extensions.entrySet()) { + if (entry.getValue() instanceof String) { + visitor.setExtension(entry.getKey(), (String) entry.getValue()); + } else if (entry.getValue() instanceof Number) { + visitor.setExtension(entry.getKey(), (Number) entry.getValue()); + } else if (entry.getValue() instanceof Boolean) { + visitor.setExtension(entry.getKey(), (Boolean) entry.getValue()); + } else { + // This should never happen because we build that map only through our builders + throw new IllegalStateException("Illegal value inside extensions map: " + entry); + } + } + + visitor.setBody(this.data); + + return visitor.end(); + } + + @Override + public T visit(StructuredMessageVisitor visitor) throws MessageVisitException, IllegalStateException { + throw Encoding.UNKNOWN_ENCODING_EXCEPTION; + } + + @Override + public void setBody(byte[] value) throws MessageVisitException { + this.data = value; + } + + @Override + public MockBinaryMessage end() { + return this; + } + + @Override + public void setAttribute(String name, String value) throws MessageVisitException { + this.attributes.put(name, value); + } + + @Override + public void setAttribute(String name, URI value) throws MessageVisitException { + this.attributes.put(name, value); + } + + @Override + public void setAttribute(String name, ZonedDateTime value) throws MessageVisitException { + this.attributes.put(name, value); + } + + @Override + public void setExtension(String name, String value) throws MessageVisitException { + this.extensions.put(name, value); + } + + @Override + public void setExtension(String name, Number value) throws MessageVisitException { + this.extensions.put(name, value); + } + + @Override + public void setExtension(String name, Boolean value) throws MessageVisitException { + this.extensions.put(name, value); + } + + @Override + public MockBinaryMessage createBinaryMessageVisitor(SpecVersion version) { + return this; + } +} diff --git a/api/src/test/java/io/cloudevents/test/Data.java b/api/src/test/java/io/cloudevents/test/Data.java new file mode 100644 index 000000000..ff305823f --- /dev/null +++ b/api/src/test/java/io/cloudevents/test/Data.java @@ -0,0 +1,55 @@ +package io.cloudevents.test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.cloudevents.CloudEvent; + +import java.net.URI; +import java.time.ZonedDateTime; +import java.util.UUID; +import java.util.stream.Stream; + +public class Data { + + public static final String ID = UUID.randomUUID().toString(); + public static final String TYPE = "mock.test"; + public static final URI SOURCE = URI.create("http://localhost/source"); + public static final String DATACONTENTTYPE_JSON = "application/json"; + public static final URI DATASCHEMA = URI.create("http://localhost/schema"); + public static final String SUBJECT = "sub"; + public static final ZonedDateTime TIME = ZonedDateTime.now(); + + public static final JsonNode DATA_JSON = new ObjectNode(JsonNodeFactory.instance); + + public static final CloudEvent V1_MIN = CloudEvent.buildV1() + .withId(ID) + .withType(TYPE) + .withSource(SOURCE) + .build(); + + public static final CloudEvent V1_WITH_JSON_DATA = CloudEvent.buildV1() + .withId(ID) + .withType(TYPE) + .withSource(SOURCE) + .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON) + .withSubject(SUBJECT) + .withTime(TIME) + .build(); + + public static Stream allEvents() { + return Stream.concat(v1Events(), v03Events()); + } + + public static Stream v1Events() { + return Stream.of( + Data.V1_MIN, + Data.V1_WITH_JSON_DATA + ); + } + + public static Stream v03Events() { + return v1Events().map(CloudEvent::toV03); + } + +} diff --git a/api/src/test/java/io/cloudevents/v03/AccessorTest.java b/api/src/test/java/io/cloudevents/v03/AccessorTest.java deleted file mode 100644 index 62de610e6..000000000 --- a/api/src/test/java/io/cloudevents/v03/AccessorTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.Collection; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; -import io.cloudevents.v03.Accessor; -import io.cloudevents.v03.CloudEventBuilder; -import io.cloudevents.v03.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class AccessorTest { - - @Test - public void should_empty_collection_when_no_extensions() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withData("my-data") - .build(); - - // act - Collection actual = Accessor.extensionsOf(ce); - - // assert - assertTrue(actual.isEmpty()); - } - - @Test - public void should_return_the_tracing_extension() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat expected = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals("0", actual.transport().get("traceparent")); - assertEquals("congo=4", actual.transport().get("tracestate")); - - assertEquals("0", - ((DistributedTracingExtension)actual.memory().getValue()).getTraceparent()); - - assertEquals("congo=4", - ((DistributedTracingExtension)actual.memory().getValue()).getTracestate()); - } - - @Test - public void should_return_the_custom_extension() { - // setup - String customExt = "comexampleextension1"; - String customVal = "my-ext-val"; - InMemoryFormat inMemory = - InMemoryFormat.of(customExt, customVal, String.class); - - ExtensionFormat expected = - ExtensionFormat.of(inMemory, customExt, customVal); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals(customVal, actual.transport().get(customExt)); - - assertEquals(String.class, actual.memory().getValueType()); - - assertEquals(customExt, actual.memory().getKey()); - - assertEquals(customVal, actual.memory().getValue()); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/CloudEventBuilderTest.java b/api/src/test/java/io/cloudevents/v03/CloudEventBuilderTest.java index 07434be04..5c3c2eff0 100644 --- a/api/src/test/java/io/cloudevents/v03/CloudEventBuilderTest.java +++ b/api/src/test/java/io/cloudevents/v03/CloudEventBuilderTest.java @@ -15,33 +15,11 @@ */ package io.cloudevents.v03; -import static io.cloudevents.v03.CloudEventBuilder.builder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.net.URI; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; -import javax.validation.executable.ExecutableValidator; -import javax.validation.metadata.BeanDescriptor; - -import io.cloudevents.validation.MockValidator; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; /** * @@ -50,206 +28,10 @@ */ public class CloudEventBuilderTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_null_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - builder() - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_empty_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - builder() - .withId("") - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_null_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - builder() - .withId("id") - .withSource(URI.create("/test")) - .build(); - } - - @Test - public void error_when_empty_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - builder() - .withId("id") - .withSource(URI.create("/test")) - .withType("") - .build(); - } - - @Test - public void error_when_null_source() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'source' must not be null"); - - // act - builder() - .withId("id") - .withType("type") - .build(); - } - - @Test - public void error_when_empty_subject() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'subject' size must be between 1 and 2147483647"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withType("type") - .withSource(URI.create("/source")) - .withSubject("") - .build(); - } - - @Test - public void error_when_invalid_encoding() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'datacontentencoding' must match \"base64\""); - - // act - CloudEventBuilder.builder() - .withId("id") - .withType("type") - .withSource(URI.create("/source")) - .withSubject("subject") - .withDatacontentencoding("binary") - .build(); - } - - @Test - public void should_have_subject() { - // act - CloudEvent ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withSubject("subject") - .build(); - - // assert - assertTrue(ce.getAttributes().getSubject().isPresent()); - assertEquals("subject", ce.getAttributes().getSubject().get()); - } - - @Test - public void should_have_dte() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - // act - CloudEventImpl ce = - builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - Object actual = ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - - // assert - assertNotNull(actual); - assertTrue(actual instanceof DistributedTracingExtension); - } - - @Test - public void should_have_custom_extension() { - String myExtKey = "comexampleextension1"; - String myExtVal = "value"; - - ExtensionFormat custom = ExtensionFormat - .of(InMemoryFormat.of(myExtKey, myExtKey, String.class), - myExtKey, myExtVal); - - // act - CloudEventImpl ce = - builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(custom) - .build(); - - Object actual = ce.getExtensions() - .get(myExtKey); - - assertNotNull(actual); - assertTrue(actual instanceof String); - } - - @Test - public void should_build_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - - CloudEventImpl event = CloudEventBuilder - .builder() - .withData(expected) - .withValidator(validator) - .build(); - - assertNotNull(event); - assertEquals(expected, event.getData().get()); - } - - @Test - public void should_build_event_from_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - CloudEvent event = CloudEventBuilder.of( - expected, - new AttributesImpl(null, null, null, null, null, null, null, null, null), - Collections.emptyList(), - validator - ); - - CloudEvent result = CloudEventBuilder - .builder(event) - .withValidator(validator) - .build(); + @ParameterizedTest() + @MethodSource("io.cloudevents.test.Data#v03Events") + void testCopyWithBuilder(CloudEvent event) { + assertThat(CloudEvent.buildV1(event).build()).isEqualTo(event); + } - assertNotNull(result); - assertEquals(expected, result.getData().get()); - } } diff --git a/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java deleted file mode 100644 index a315326df..000000000 --- a/api/src/test/java/io/cloudevents/v03/CloudEventJacksonTest.java +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.InputStream; -import java.net.URI; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import com.fasterxml.jackson.core.type.TypeReference; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.json.Json; -import io.cloudevents.json.types.Much; - -/** - * - * @author fabiojose - * - */ -public class CloudEventJacksonTest { - - private static InputStream resourceOf(String name) { - return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); - } - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void should_encode_right_with_minimal_attrs() { - // setup - CloudEvent ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("x10")); - assertTrue(json.contains("/source")); - assertTrue(json.contains("event-type")); - assertTrue(json.contains("0.3")); - - assertFalse(json.contains("time")); - assertFalse(json.contains("schemaurl")); - assertFalse(json.contains("contenttype")); - assertFalse(json.contains("data")); - } - - @Test - public void should_have_optional_attrs() { - // setup - CloudEvent ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withDatacontentencoding("base64") - .withSubject("subject0") - .withData("my-data") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("/schema")); - assertTrue(json.contains("text/plain")); - assertTrue(json.contains("my-data")); - assertTrue(json.contains("\"base64\"")); - assertTrue(json.contains("subject0")); - - assertTrue(json.contains("\"schemaurl\"")); - assertTrue(json.contains("datacontenttype")); - assertTrue(json.contains("datacontentencoding")); - assertTrue(json.contains("\"subject\"")); - } - - @Test - public void should_serialize_trace_extension() { - // setup - String expected = "\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}"; - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEvent ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withData("my-data") - .withExtension(tracing) - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertTrue(actual.contains(expected)); - } - - @Test - public void should_not_serialize_attributes_element() { - // setup - CloudEvent ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSchemaurl(URI.create("/schema")) - .withDatacontenttype("text/plain") - .withSubject("subject0") - .withData("my-data") - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertFalse(actual.contains("\"attributes\"")); - } - - @Test - public void should_have_type() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertEquals("aws.s3.object.created", ce.getAttributes().getType()); - } - - @Test - public void should_have_id() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertEquals("C234-1234-1234", ce.getAttributes().getId()); - } - - //should have time - @Test - public void should_have_time() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getTime().isPresent()); - } - - @Test - public void should_have_source() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource()); - } - - @Test - public void should_have_datacontenttype() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getDatacontenttype().isPresent()); - assertEquals("application/json", ce.getAttributes().getDatacontenttype().get()); - } - - @Test - public void should_have_datacontentencoding() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_base64.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getDatacontentencoding().isPresent()); - assertEquals("base64", ce.getAttributes().getDatacontentencoding().get()); - } - - @Test - public void should_have_specversion() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class); - - // assert - assertEquals("0.3", ce.getAttributes().getSpecVersion()); - } - - @Test - public void should_throw_when_absent() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - Json.fromInputStream(resourceOf("03_absent.json"), CloudEventImpl.class); - } - - @Test - public void should_have_tracing_extension() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_extension.json"), CloudEventImpl.class); - - // assert - assertNotNull(ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - } - - @Test - public void should_have_custom_extension() { - // setup - String extensionKey = "my-extension"; - String expected = "extension-value"; - - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("03_extension.json"), CloudEventImpl.class); - - // assert - assertEquals(expected, ce.getExtensions() - .get(extensionKey)); - } - - @Test - public void should_have_custom_data() { - // setup - Much expected = new Much(); - expected.setWow("kinda"); - - String json = "{\"type\":\"aws.s3.object.created\",\"id\":\"C234-1234-1234\",\"time\":\"2019-08-19T19:35:00.000Z\",\"source\":\"https://serverless.com\",\"datacontenttype\":\"application/json\",\"specversion\":\"0.3\",\"data\":{\"wow\":\"kinda\"}}"; - - // act - CloudEvent ce = - Json.decodeValue(json, new TypeReference>() {}); - - // assert - assertTrue(ce.getData().isPresent()); - assertEquals(expected.getWow(), ce.getData().get().getWow()); - } - -} diff --git a/api/src/test/java/io/cloudevents/v03/http/AttributeMapperTest.java b/api/src/test/java/io/cloudevents/v03/http/AttributeMapperTest.java deleted file mode 100644 index b45a41e5f..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/AttributeMapperTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.v03.http.AttributeMapper; - -/** - * - * @author fabiojose - * - */ -public class AttributeMapperTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - AttributeMapper.map(null); - } - - @Test - public void should_not_map_null_value() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-type", null); - - String expected = "type"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.containsKey(expected)); - } - - @Test - public void should_ok_when_no_content_type() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "0.3"); - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.isEmpty()); - } - - @Test - public void should_map_cespecversion_to_specversion() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "0.3"); - headers.put("Content-Type", "application/json"); - - String expected = "specversion"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertNotNull(attributes.get(expected)); - } - - @Test - public void should_all_without_prefix_ce() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.3"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - Map actual = AttributeMapper.map(myHeaders); - - actual.keySet() - .forEach((attribute) -> { - assertFalse(attribute.startsWith("ce-")); - }); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/ExtensionMapperTest.java b/api/src/test/java/io/cloudevents/v03/http/ExtensionMapperTest.java deleted file mode 100644 index 6b2e11327..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/ExtensionMapperTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.v03.http.ExtensionMapper; - -/** - * - * @author fabiojose - * - */ -public class ExtensionMapperTest { - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - ExtensionMapper.map(null); - } - - @Test - public void should_not_map_null_values() { - //setuṕ - String expected = "nullexp"; - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.3"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - myHeaders.put(expected, null); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - - // assert - assertFalse(actual.containsKey(expected)); - } - - @Test - public void should_return_just_potential_extensions() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.3"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - // asset - assertFalse(actual.isEmpty()); - assertEquals(3, actual.keySet().size()); - actual.keySet() - .forEach(header -> { - assertFalse(header.startsWith("ce-")); - }); - - assertEquals("0", actual.get("traceparent")); - assertEquals("congo=4", actual.get("tracestate")); - assertEquals("myextension", actual.get("my-ext")); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryMarshallerTest.java b/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryMarshallerTest.java deleted file mode 100644 index f35a13435..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryMarshallerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v03.CloudEventBuilder; -import io.cloudevents.v03.CloudEventImpl; -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryMarshallerTest { - @Test - public void should_marshal_data_as_json() { - // setup - String expected = "{\"wow\":\"yes!\"}"; - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_attributes_as_headers() { - // setup - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertEquals(ce.getAttributes().getId(), actual.getHeaders().get("ce-id")); - assertEquals(ce.getAttributes().getSource(), URI.create(actual.getHeaders().get("ce-source"))); - assertEquals(ce.getAttributes().getType(), actual.getHeaders().get("ce-type")); - assertEquals(ce.getAttributes().getSubject().get(), actual.getHeaders().get("ce-subject")); - assertEquals(ce.getAttributes().getDatacontenttype().get(), actual.getHeaders().get("Content-Type")); - } - - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java deleted file mode 100644 index 32d1bd7b1..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPBinaryUnmarshallerTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v03.AttributesImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryUnmarshallerTest { - @Test - public void should_unmarshal_headers_and_json_payload() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("ce-subject", "subject"); - myHeaders.put("Content-Type", "application/json"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertEquals("0x11", actual.getAttributes().getId()); - assertEquals(URI.create("/source"), actual.getAttributes().getSource()); - assertEquals("0.3", actual.getAttributes().getSpecVersion()); - assertEquals("br.my", actual.getAttributes().getType()); - assertTrue(actual.getAttributes().getTime().isPresent()); - assertTrue(actual.getAttributes().getSchemaurl().isPresent()); - assertEquals(URI.create("http://my.br"), actual.getAttributes().getSchemaurl().get()); - assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); - assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); - assertTrue(actual.getData().isPresent()); - assertEquals(expected, actual.getData().get()); - assertTrue(actual.getAttributes().getSubject().isPresent()); - assertEquals("subject", actual.getAttributes().getSubject().get()); - } - - @Test - public void should_unmarshal_tracing_extension_from_header() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "0.2"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - myHeaders.put("traceparent", "0x200"); - myHeaders.put("tracestate", "congo=9"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertNotNull(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - assertTrue(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredMarshallerTest.java b/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredMarshallerTest.java deleted file mode 100644 index ffadccfb3..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredMarshallerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v03.CloudEventBuilder; -import io.cloudevents.v03.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredMarshallerTest { - @Test - public void should_marshal_all_as_json() { - // setup - String expected = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_data_as_text_and_evelope_as_json() { - // setup - String expected = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_headers_have_content_type() { - // setup - String expected = "application/cloudevents+json"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertTrue(actual.getHeaders().containsKey("Content-Type")); - assertEquals(expected, actual.getHeaders().get("Content-Type")); - } - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java b/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java deleted file mode 100644 index b40615d14..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/HTTPStructuredUnmarshaller.java +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v03.AttributesImpl; -import io.cloudevents.v03.CloudEventBuilder; -import io.cloudevents.v03.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredUnmarshaller { - @Test - public void should_unmarshal_json_envelope_and_json_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(Much.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecVersion(), - actual.getAttributes().getSpecVersion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - - assertTrue(actual.getAttributes().getSubject().isPresent()); - assertEquals(expected.getAttributes().getSubject().get(), - actual.getAttributes().getSubject().get()); - } - - @Test - public void should_unmarshal_json_envelope_and_text_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDatacontenttype("text/plain") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecVersion(), - actual.getAttributes().getSpecVersion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - } - - @Test - public void should_unmarshal_the_tracing_extension_from_headers() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - httpHeaders.put("traceparent", "0x200"); - httpHeaders.put("tracestate", "congo=9"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.3\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertTrue(actual.getExtensions().containsKey( - DistributedTracingExtension.Format.IN_MEMORY_KEY)); - - assertTrue(actual.getExtensions().get( - DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v03/http/HeaderMapperTest.java b/api/src/test/java/io/cloudevents/v03/http/HeaderMapperTest.java deleted file mode 100644 index 95941944a..000000000 --- a/api/src/test/java/io/cloudevents/v03/http/HeaderMapperTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v03.http; - -import static org.junit.Assert.assertFalse; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapperTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_attributes_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map extensions = new HashMap<>(); - - // act - HeaderMapper.map(null, extensions); - } - - @Test - public void error_when_extensions_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map attributes = new HashMap<>(); - - // act - HeaderMapper.map(attributes, null); - } - - @Test - public void should_not_map_null_attribute_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", null); - attributes.put("specversion", "0.3"); - - Map extensions = new HashMap<>(); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("ce-type")); - } - - @Test - public void should_not_map_null_extension_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", null); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("ce-null-ext")); - } - - @Test - public void should_not_map_absent_datacontenttype() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "0.2"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", "null-value"); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("Content-Type")); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/AccessorTest.java b/api/src/test/java/io/cloudevents/v1/AccessorTest.java deleted file mode 100644 index 429eac9dc..000000000 --- a/api/src/test/java/io/cloudevents/v1/AccessorTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.Collection; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; -import io.cloudevents.v1.Accessor; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class AccessorTest { - @Test - public void should_empty_collection_when_no_extensions() { - // setup - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") - .withData("my-data") - .build(); - - // act - Collection actual = Accessor.extensionsOf(ce); - - // assert - assertTrue(actual.isEmpty()); - } - - @Test - public void should_return_the_tracing_extension() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat expected = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals("0", actual.transport().get("traceparent")); - assertEquals("congo=4", actual.transport().get("tracestate")); - - assertEquals("0", - ((DistributedTracingExtension)actual.memory().getValue()).getTraceparent()); - - assertEquals("congo=4", - ((DistributedTracingExtension)actual.memory().getValue()).getTracestate()); - } - - @Test - public void should_return_the_custom_extension() { - // setup - String customExt = "comexampleextension1"; - String customVal = "my-ext-val"; - InMemoryFormat inMemory = - InMemoryFormat.of(customExt, customVal, String.class); - - ExtensionFormat expected = - ExtensionFormat.of(inMemory, customExt, customVal); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") - .withData("my-data") - .withExtension(expected) - .build(); - - // act - Collection extensions = - Accessor.extensionsOf(ce); - - // assert - assertFalse(extensions.isEmpty()); - ExtensionFormat actual = extensions.iterator().next(); - - assertEquals(customVal, actual.transport().get(customExt)); - - assertEquals(String.class, actual.memory().getValueType()); - - assertEquals(customExt, actual.memory().getKey()); - - assertEquals(customVal, actual.memory().getValue()); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/CloudEventBuilderTest.java b/api/src/test/java/io/cloudevents/v1/CloudEventBuilderTest.java index 683b88f95..af4b43c02 100644 --- a/api/src/test/java/io/cloudevents/v1/CloudEventBuilderTest.java +++ b/api/src/test/java/io/cloudevents/v1/CloudEventBuilderTest.java @@ -15,218 +15,24 @@ */ package io.cloudevents.v1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.net.URI; -import java.util.Collections; - -import javax.validation.Validator; +import io.cloudevents.CloudEvent; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; -import io.cloudevents.validation.MockValidator; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import static org.assertj.core.api.Assertions.assertThat; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.extensions.InMemoryFormat; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; /** - * + * * @author fabiojose * */ public class CloudEventBuilderTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_null_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - CloudEventBuilder.builder() - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_empty_id() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("") - .withSource(URI.create("/test")) - .withType("type") - .build(); - } - - @Test - public void error_when_null_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/test")) - .build(); - } - - @Test - public void error_when_empty_type() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'type' must not be blank"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/test")) - .withType("") - .build(); - } - - @Test - public void error_when_null_source() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'source' must not be null"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withType("type") - .build(); - } - - @Test - public void error_when_empty_subject() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'subject' size must be between 1 and 2147483647"); - - // act - CloudEventBuilder.builder() - .withId("id") - .withType("type") - .withSource(URI.create("/source")) - .withSubject("") - .build(); - } - - @Test - public void should_have_subject() { - // act - CloudEvent ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withSubject("subject") - .build(); - - // assert - assertTrue(ce.getAttributes().getSubject().isPresent()); - assertEquals("subject", ce.getAttributes().getSubject().get()); - } - - @Test - public void should_have_dte() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - Object actual = ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY); - - // assert - assertNotNull(actual); - assertTrue(actual instanceof DistributedTracingExtension); - } - - @Test - public void should_have_custom_extension() { - String myExtKey = "comexampleextension1"; - String myExtVal = "value"; - - ExtensionFormat custom = ExtensionFormat - .of(InMemoryFormat.of(myExtKey, myExtKey, String.class), - myExtKey, myExtVal); - - // act - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(custom) - .build(); - - Object actual = ce.getExtensions() - .get(myExtKey); - - assertNotNull(actual); - assertTrue(actual instanceof String); - } - - @Test - public void should_build_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - - CloudEventImpl event = CloudEventBuilder - .builder() - .withData(expected) - .withValidator(validator) - .build(); - - assertNotNull(event); - assertEquals(expected, event.getData().get()); - } - - @Test - public void should_build_event_from_event_using_custom_validator() { - Validator validator = new MockValidator(); - String expected = "test"; - CloudEvent event = CloudEventBuilder.builder() - .withData(expected) - .withValidator(validator) - .build(); - - CloudEvent result = CloudEventBuilder - .builder(event) - .withValidator(validator) - .build(); + @ParameterizedTest() + @MethodSource("io.cloudevents.test.Data#v1Events") + void testCopyWithBuilder(CloudEvent event) { + assertThat(CloudEvent.buildV1(event).build()).isEqualTo(event); + } - assertNotNull(result); - assertEquals(expected, result.getData().get()); - } } diff --git a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java deleted file mode 100644 index a893e20eb..000000000 --- a/api/src/test/java/io/cloudevents/v1/CloudEventJacksonTest.java +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1; - -import com.fasterxml.jackson.core.type.TypeReference; -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.json.Json; -import io.cloudevents.json.types.Much; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import java.io.InputStream; -import java.net.URI; -import java.time.ZonedDateTime; -import java.util.Base64; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.junit.Assert.*; - -/** - * - * @author fabiojose - * - */ -public class CloudEventJacksonTest { - - private static InputStream resourceOf(String name) { - return Thread.currentThread().getContextClassLoader().getResourceAsStream(name); - } - - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void should_encode_right_with_minimal_attrs() { - // setup - CloudEvent ce = - CloudEvent.buildV1() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("x10")); - assertTrue(json.contains("/source")); - assertTrue(json.contains("event-type")); - assertTrue(json.contains("1.0")); - - assertFalse(json.contains("time")); - assertFalse(json.contains("schemaurl")); - assertFalse(json.contains("contenttype")); - assertFalse(json.contains("data")); - } - - @Test - public void should_have_optional_attrs() { - // setup - CloudEvent ce = - CloudEvent.buildV1() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withSubject("subject0") - .withData("text/plain", URI.create("/schema"), "my-data") - .build(); - - // act - String json = Json.encode(ce); - - // assert - assertTrue(json.contains("/schema")); - assertTrue(json.contains("text/plain")); - assertTrue(json.contains("my-data")); - assertTrue(json.contains("subject0")); - - assertTrue(json.contains("\"dataschema\"")); - assertTrue(json.contains("datacontenttype")); - assertTrue(json.contains("\"subject\"")); - - Pattern pat = Pattern.compile("(\"data\")"); - Matcher mat = pat.matcher(json); - int counter = 0; - while(mat.find()) { - counter++; - } - assertEquals(1, counter); - } - - @Test - public void should_serialize_trace_extension() { - // setup - String expected = "\"distributedTracing\":{\"traceparent\":\"0\",\"tracestate\":\"congo=4\"}"; - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEvent ce = - CloudEvent.buildV1() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") - .withData("my-data") - .withExtension(tracing) - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertTrue(actual.contains(expected)); - } - - @Test - public void should_not_serialize_attributes_element() { - // setup - CloudEvent ce = - CloudEvent.buildV1() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataschema(URI.create("/schema")) - .withDataContentType("text/plain") - .withSubject("subject0") - .withData("my-data") - .build(); - - // act - String actual = Json.encode(ce); - - // assert - assertFalse(actual.contains("\"attributes\"")); - } - - @Test - public void should_have_type() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertEquals("aws.s3.object.created", ce.getAttributes().getType()); - } - - @Test - public void should_have_id() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertEquals("C234-1234-1234", ce.getAttributes().getId()); - } - - //should have time - @Test - public void should_have_time() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getTime().isPresent()); - } - - @Test - public void should_have_source() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource()); - } - - @Test - public void should_have_datacontenttype() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getDatacontenttype().isPresent()); - assertEquals("application/json", ce.getAttributes().getDatacontenttype().get()); - } - - @Test - public void should_have_dataschema() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertTrue(ce.getAttributes().getDataschema().isPresent()); - assertEquals(URI.create("/my-schema"), ce.getAttributes().getDataschema().get()); - } - - @Test - public void should_have_specversion() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_new.json"), CloudEventImpl.class); - - // assert - assertEquals("1.0", ce.getAttributes().getSpecVersion()); - } - - @Test - public void should_throw_when_absent() { - // setup - expectedEx.expect(IllegalStateException.class); - expectedEx.expectMessage("invalid payload: 'id' must not be blank"); - - // act - Json.fromInputStream(resourceOf("1_absent.json"), CloudEventImpl.class); - } - - @Test - public void should_have_tracing_extension() { - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); - - // assert - assertNotNull(ce.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - } - - @Test - public void should_have_custom_extension() { - // setup - String extensionKey = "my-extension"; - String expected = "extension-value"; - - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_extension.json"), CloudEventImpl.class); - - // assert - assertEquals(expected, ce.getExtensions() - .get(extensionKey)); - } - - @Test - public void should_have_custom_data() { - // setup - Much expected = new Much(); - expected.setWow("kinda"); - - String json = "{\"type\":\"aws.s3.object.created\",\"id\":\"C234-1234-1234\",\"time\":\"2019-08-19T19:35:00.000Z\",\"source\":\"https://serverless.com\",\"datacontenttype\":\"application/json\",\"specversion\":\"1.0\",\"data\":{\"wow\":\"kinda\"}}"; - - // act - CloudEvent ce = - Json.decodeValue(json, new TypeReference>() {}); - - // assert - assertTrue(ce.getData().isPresent()); - assertEquals(expected.getWow(), ce.getData().get().getWow()); - } - - @Test - public void should_unmarshal_data_base64() { - // setup - byte[] expected = "mydata".getBytes(); - - // act - CloudEvent ce = - Json.fromInputStream(resourceOf("1_base64.json"), - new TypeReference>() {}); - - // assert - assertNotNull(ce.getDataBase64()); - assertArrayEquals(expected, ce.getDataBase64()); - } - - @Test - public void should_marshal_data_byte_array_as_data_base64() { - // setup - byte[] data = ("--mydata--" - + "\n" - + "customer=445" - + "\n" - + "invoice=5566" - + "\n" - + "---mydata---").getBytes(); - - String expected = - Base64.getEncoder().encodeToString(data); - - CloudEventImpl event = - CloudEventBuilder.builder() - .withId("0xbin") - .withSource(URI.create("/customers/445")) - .withType("customers.ordering") - .withDataContentType("text/plain") - .withDataschema(URI.create("http://schame.server.com/customer/order")) - .withSubject("orders.json") - .withTime(ZonedDateTime.now()) - .withDataBase64(data) - .build(); - - // act - String encoded = Json.encode(event); - - // assert - assertTrue(encoded.contains("\"data_base64\"")); - assertTrue(encoded.contains("\"" + expected +"\"")); - } - -} diff --git a/api/src/test/java/io/cloudevents/v1/http/AttributeMapperTest.java b/api/src/test/java/io/cloudevents/v1/http/AttributeMapperTest.java deleted file mode 100644 index 65f1a2137..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/AttributeMapperTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.v1.http.AttributeMapper; - -/** - * - * @author fabiojose - * - */ -public class AttributeMapperTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - AttributeMapper.map(null); - } - - @Test - public void should_not_map_null_value() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-type", null); - - String expected = "type"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.containsKey(expected)); - } - - @Test - public void should_ok_when_no_content_type() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "1.0"); - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertFalse(attributes.isEmpty()); - } - - @Test - public void should_map_cespecversion_to_specversion() { - // setup - Map headers = new HashMap<>(); - headers.put("ce-specversion", "1.0"); - headers.put("Content-Type", "application/json"); - - String expected = "specversion"; - - // act - Map attributes = - AttributeMapper.map(headers); - - // assert - assertNotNull(attributes.get(expected)); - } - - @Test - public void should_all_without_prefix_ce() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "1.0"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-dataschema", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - Map actual = AttributeMapper.map(myHeaders); - - actual.keySet() - .forEach((attribute) -> { - assertFalse(attribute.startsWith("ce-")); - }); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/ExtensionMapperTest.java b/api/src/test/java/io/cloudevents/v1/http/ExtensionMapperTest.java deleted file mode 100644 index ad567d215..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/ExtensionMapperTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.v1.http.ExtensionMapper; - -/** - * - * @author fabiojose - * - */ -public class ExtensionMapperTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_headers_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - // act - ExtensionMapper.map(null); - } - - @Test - public void should_not_map_null_values() { - //setuṕ - String expected = "nullexp"; - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "1.0"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-dataschema", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - myHeaders.put(expected, null); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - - // assert - assertFalse(actual.containsKey(expected)); - } - - @Test - public void should_return_just_potential_extensions() { - // setup - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "1.0"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-dataschema", "http://my.br"); - myHeaders.put("my-ext", "myextension"); - myHeaders.put("traceparent", "0"); - myHeaders.put("tracestate", "congo=4"); - myHeaders.put("Content-Type", "application/json"); - - // act - Map actual = ExtensionMapper.map(myHeaders); - - // asset - assertFalse(actual.isEmpty()); - assertEquals(3, actual.keySet().size()); - actual.keySet() - .forEach(header -> { - assertFalse(header.startsWith("ce-")); - }); - - assertEquals("0", actual.get("traceparent")); - assertEquals("congo=4", actual.get("tracestate")); - assertEquals("myextension", actual.get("my-ext")); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryMarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryMarshallerTest.java deleted file mode 100644 index 9a921b91a..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryMarshallerTest.java +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.util.Base64; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; -import io.cloudevents.v1.http.Marshallers; - -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryMarshallerTest { - @Test - public void should_marshal_data_as_json() { - // setup - String expected = "{\"wow\":\"yes!\"}"; - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_byte_array_as_base64() { - // setup - - byte[] data = "my-data".getBytes(); - String expected = "\"" + - Base64.getEncoder().encodeToString(data) + "\""; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("text/plain") - .withSubject("subject") - .withData(data) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertTrue(actual.getPayload().isPresent()); - assertEquals(expected, actual.getPayload().get()); - } - - @Test - public void should_marshal_attributes_as_headers() { - // setup - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertEquals(ce.getAttributes().getId(), actual.getHeaders().get("ce-id")); - assertEquals(ce.getAttributes().getSource(), URI.create(actual.getHeaders().get("ce-source"))); - assertEquals(ce.getAttributes().getType(), actual.getHeaders().get("ce-type")); - assertEquals(ce.getAttributes().getSubject().get(), actual.getHeaders().get("ce-subject")); - assertEquals(ce.getAttributes().getDatacontenttype().get(), actual.getHeaders().get("Content-Type")); - } - - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers. - binary() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java deleted file mode 100644 index fb0a27d14..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPBinaryUnmarshallerTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import java.net.URI; -import java.util.HashMap; -import java.util.Map; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v1.AttributesImpl; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -/** - * - * @author fabiojose - * - */ -public class HTTPBinaryUnmarshallerTest { - @Test - public void should_unmarshal_headers_and_json_payload() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "1.0"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-dataschema", "http://my.br"); - myHeaders.put("ce-subject", "subject"); - myHeaders.put("Content-Type", "application/json"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertEquals("0x11", actual.getAttributes().getId()); - assertEquals(URI.create("/source"), actual.getAttributes().getSource()); - assertEquals("1.0", actual.getAttributes().getSpecVersion()); - assertEquals("br.my", actual.getAttributes().getType()); - assertTrue(actual.getAttributes().getTime().isPresent()); - assertTrue(actual.getAttributes().getDataschema().isPresent()); - assertEquals(URI.create("http://my.br"), actual.getAttributes().getDataschema().get()); - assertTrue(actual.getAttributes().getDatacontenttype().isPresent()); - assertEquals("application/json", actual.getAttributes().getDatacontenttype().get()); - assertTrue(actual.getData().isPresent()); - assertEquals(expected, actual.getData().get()); - assertTrue(actual.getAttributes().getSubject().isPresent()); - assertEquals("subject", actual.getAttributes().getSubject().get()); - } - - @Test - public void should_unmarshal_tracing_extension_from_header() { - // setup - Much expected = new Much(); - expected.setWow("yes!"); - - Map myHeaders = new HashMap<>(); - myHeaders.put("ce-id", "0x11"); - myHeaders.put("ce-source", "/source"); - myHeaders.put("ce-specversion", "1.0"); - myHeaders.put("ce-type", "br.my"); - myHeaders.put("ce-time", "2019-09-16T20:49:00Z"); - myHeaders.put("ce-schemaurl", "http://my.br"); - myHeaders.put("Content-Type", "application/json"); - - myHeaders.put("traceparent", "0x200"); - myHeaders.put("tracestate", "congo=9"); - - String payload = "{\"wow\":\"yes!\"}"; - - // act - CloudEvent actual = - Unmarshallers.binary(Much.class) - .withHeaders(() -> myHeaders) - .withPayload(() -> payload) - .unmarshal(); - - // assert - assertNotNull(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY)); - assertTrue(actual.getExtensions() - .get(DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java deleted file mode 100644 index 39c8c6bc9..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredMarshallerTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.net.URI; - -import org.junit.Test; - -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; -import io.cloudevents.format.Wire; -import io.cloudevents.json.types.Much; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; -import io.cloudevents.v1.http.Marshallers; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredMarshallerTest { - - @Test - public void should_headers_have_content_type() { - // setup - String expected = "application/cloudevents+json"; - String ceData = "yes!"; - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("text/plain") - .withData(ceData) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - assertFalse(actual.getHeaders().isEmpty()); - assertTrue(actual.getHeaders().containsKey("Content-Type")); - assertEquals(expected, actual.getHeaders().get("Content-Type")); - } - - @Test - public void should_marshal_the_tracing_extension_as_header() { - // setup - final DistributedTracingExtension dt = new DistributedTracingExtension(); - dt.setTraceparent("0"); - dt.setTracestate("congo=4"); - - final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt); - - CloudEventImpl ce = - CloudEventBuilder.builder() - .withId("id") - .withSource(URI.create("/source")) - .withType("type") - .withExtension(tracing) - .build(); - - // act - Wire actual = - Marshallers.structured() - .withEvent(() -> ce) - .marshal(); - - // assert - assertFalse(actual.getHeaders().isEmpty()); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_PARENT_KEY)); - assertNotNull(actual.getHeaders().get(DistributedTracingExtension - .Format.TRACE_STATE_KEY)); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java b/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java deleted file mode 100644 index c80bda991..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/HTTPStructuredUnmarshallerTest.java +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; - -import java.net.URI; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import io.cloudevents.CloudEvent; -import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.json.types.Much; -import io.cloudevents.v1.AttributesImpl; -import io.cloudevents.v1.CloudEventBuilder; -import io.cloudevents.v1.CloudEventImpl; - -/** - * - * @author fabiojose - * - */ -public class HTTPStructuredUnmarshallerTest { - @Test - public void should_unmarshal_json_envelope_and_json_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"application/json\",\"subject\":\"subject\"}"; - - Much ceData = new Much(); - ceData.setWow("yes!"); - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("application/json") - .withSubject("subject") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(Much.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecVersion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - - assertTrue(actual.getAttributes().getSubject().isPresent()); - assertEquals(expected.getAttributes().getSubject().get(), - actual.getAttributes().getSubject().get()); - } - - @Test - public void should_unmarshal_json_envelope_and_text_data() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - String ceData = "yes!"; - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("text/plain") - .withData(ceData) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecVersion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertTrue(actual.getData().isPresent()); - assertEquals(expected.getData().get(), actual.getData().get()); - } - - @Test - public void should_unmarshal_json_envelope_and_text_data_base64() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - String ceData = "yes!"; - byte[] base64Data = Base64.getEncoder().encode(ceData.getBytes()); - String json = "{\"data_base64\":\"" + new String(base64Data) + "\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - - CloudEventImpl expected = - CloudEventBuilder.builder() - .withId("x10") - .withSource(URI.create("/source")) - .withType("event-type") - .withDataContentType("text/plain") - .withDataBase64(ceData.getBytes()) - .build(); - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertEquals(expected.getAttributes().getSpecversion(), - actual.getAttributes().getSpecVersion()); - - assertEquals(expected.getAttributes().getId(), - actual.getAttributes().getId()); - - assertEquals(expected.getAttributes().getSource(), - actual.getAttributes().getSource()); - - assertEquals(expected.getAttributes().getType(), - actual.getAttributes().getType()); - - assertFalse(actual.getData().isPresent()); - assertNotNull(actual.getDataBase64()); - assertEquals(new String(expected.getDataBase64()), new String(actual.getDataBase64())); - } - - @Test - public void should_unmarshal_the_tracing_extension_from_headers() { - // setup - Map httpHeaders = new HashMap<>(); - httpHeaders.put("Content-Type", "application/cloudevents+json"); - - httpHeaders.put("traceparent", "0x200"); - httpHeaders.put("tracestate", "congo=9"); - - String json = "{\"data\":\"yes!\",\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"1.0\",\"type\":\"event-type\",\"datacontenttype\":\"text/plain\"}"; - - // act - CloudEvent actual = - Unmarshallers.structured(String.class) - .withHeaders(() -> httpHeaders) - .withPayload(() -> json) - .unmarshal(); - - // assert - assertTrue(actual.getExtensions().containsKey( - DistributedTracingExtension.Format.IN_MEMORY_KEY)); - - assertTrue(actual.getExtensions().get( - DistributedTracingExtension.Format.IN_MEMORY_KEY) - instanceof DistributedTracingExtension); - } -} diff --git a/api/src/test/java/io/cloudevents/v1/http/HeaderMapperTest.java b/api/src/test/java/io/cloudevents/v1/http/HeaderMapperTest.java deleted file mode 100644 index 30761604e..000000000 --- a/api/src/test/java/io/cloudevents/v1/http/HeaderMapperTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2019 The CloudEvents Authors - * - * Licensed 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 io.cloudevents.v1.http; - -import static org.junit.Assert.assertFalse; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import io.cloudevents.v1.http.HeaderMapper; - -/** - * - * @author fabiojose - * - */ -public class HeaderMapperTest { - @Rule - public ExpectedException expectedEx = ExpectedException.none(); - - @Test - public void error_when_attributes_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map extensions = new HashMap<>(); - - // act - HeaderMapper.map(null, extensions); - } - - @Test - public void error_when_extensions_map_isnull() { - // setup - expectedEx.expect(NullPointerException.class); - - Map attributes = new HashMap<>(); - - // act - HeaderMapper.map(attributes, null); - } - - @Test - public void should_not_map_null_attribute_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", null); - attributes.put("specversion", "1.0"); - - Map extensions = new HashMap<>(); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("ce-type")); - } - - @Test - public void should_not_map_null_extension_value() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "1.0"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", null); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("ce-null-ext")); - } - - @Test - public void should_not_map_absent_datacontenttype() { - // setup - Map attributes = new HashMap<>(); - attributes.put("type", "mytype"); - attributes.put("specversion", "1.0"); - - Map extensions = new HashMap<>(); - extensions.put("null-ext", "null-value"); - extensions.put("comexampleextension1", "value"); - - // act - Map actual = HeaderMapper.map(attributes, extensions); - - //assert - assertFalse(actual.containsKey("Content-Type")); - } -} diff --git a/api/src/test/java/io/cloudevents/validation/MockValidator.java b/api/src/test/java/io/cloudevents/validation/MockValidator.java deleted file mode 100644 index d50d9dd92..000000000 --- a/api/src/test/java/io/cloudevents/validation/MockValidator.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.cloudevents.validation; - -import java.util.HashSet; -import java.util.Set; - -import javax.validation.ConstraintViolation; -import javax.validation.Validator; -import javax.validation.executable.ExecutableValidator; -import javax.validation.metadata.BeanDescriptor; - -public class MockValidator implements Validator { - - @Override - public Set> validate(T object, Class... groups) { - return new HashSet<>(); - } - - @Override - public Set> validateProperty(T object, String propertyName, Class... groups) { - return null; - } - - @Override - public Set> validateValue(Class beanType, String propertyName, Object value, Class... groups) { - return null; - } - - @Override - public BeanDescriptor getConstraintsForClass(Class clazz) { - return null; - } - - @Override - public T unwrap(Class type) { - return null; - } - - @Override - public ExecutableValidator forExecutables() { - return null; - } -} \ No newline at end of file diff --git a/kafka/src/main/java/io/cloudevents/v1/kafka/Unmarshallers.java b/kafka/src/main/java/io/cloudevents/v1/kafka/Unmarshallers.java index 00f3647f9..66710d0fc 100644 --- a/kafka/src/main/java/io/cloudevents/v1/kafka/Unmarshallers.java +++ b/kafka/src/main/java/io/cloudevents/v1/kafka/Unmarshallers.java @@ -1,19 +1,8 @@ package io.cloudevents.v1.kafka; -import static io.cloudevents.extensions.DistributedTracingExtension.Format.IN_MEMORY_KEY; -import static io.cloudevents.extensions.DistributedTracingExtension.Format.TRACE_PARENT_KEY; -import static io.cloudevents.extensions.DistributedTracingExtension.Format.TRACE_STATE_KEY; -import static java.util.Optional.ofNullable; - -import java.util.Map; -import java.util.Optional; -import java.util.AbstractMap.SimpleEntry; -import java.util.Map.Entry; -import java.util.stream.Collectors; - import io.cloudevents.extensions.DistributedTracingExtension; -import io.cloudevents.extensions.ExtensionFormat; import io.cloudevents.extensions.DistributedTracingExtension.Format; +import io.cloudevents.extensions.ExtensionFormat; import io.cloudevents.format.BinaryUnmarshaller; import io.cloudevents.format.StructuredUnmarshaller; import io.cloudevents.format.builder.HeadersStep; @@ -21,30 +10,37 @@ import io.cloudevents.v1.AttributesImpl; import io.cloudevents.v1.CloudEventBuilder; import io.cloudevents.v1.CloudEventImpl; -import io.cloudevents.v1.kafka.AttributeMapper; -import io.cloudevents.v1.kafka.ExtensionMapper; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.stream.Collectors; + +import static io.cloudevents.extensions.DistributedTracingExtension.Format.*; +import static java.util.Optional.ofNullable; /** - * + * * @author fabiojose * @version 1.0 */ public class Unmarshallers { private Unmarshallers() {} - + /** * Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data * for Kafka Transport Binding - * + * * @param The 'data' type * @param type The type reference to use for 'data' unmarshal * @return A step to supply the headers, payload and to unmarshal * @see BinaryUnmarshaller */ - public static HeadersStep + public static HeadersStep binary(Class type) { - - return + + return BinaryUnmarshaller. builder() .map(AttributeMapper::map) @@ -57,66 +53,66 @@ private Unmarshallers() {} .builder(CloudEventBuilder.builder()::build); } - + /** * Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data * for Kafka Transport Binding - * + * * @param The 'data' type * @param typeOfData The type reference to use for 'data' unmarshal * @return A step to supply the headers, payload and to unmarshal * @see StructuredUnmarshaller */ @SuppressWarnings("unchecked") - public static HeadersStep + public static HeadersStep structured(Class typeOfData) { - + return StructuredUnmarshaller. builder() .map(ExtensionMapper::map) .map(DistributedTracingExtension::unmarshall) .next() - .map((payload, extensions) -> { + .map((payload, extensions) -> { CloudEventImpl event = Json.> binaryDecodeValue(payload, CloudEventImpl.class, typeOfData); - - Optional dteFormat = + + Optional dteFormat = ofNullable(event.getExtensions().get(IN_MEMORY_KEY)) .filter(extension -> extension instanceof Map) .map(extension -> (Map)extension) - .map(extension -> + .map(extension -> extension.entrySet() .stream() - .filter(entry -> - null!= entry.getKey() + .filter(entry -> + null!= entry.getKey() && null!= entry.getValue()) - .map(tracing -> - new SimpleEntry<>(tracing.getKey(), + .map(tracing -> + new SimpleEntry<>(tracing.getKey(), tracing.getValue().toString())) .collect(Collectors.toMap(Entry::getKey, Entry::getValue))) .map(extension -> { - DistributedTracingExtension dte = + DistributedTracingExtension dte = new DistributedTracingExtension(); dte.setTraceparent(extension.get(TRACE_PARENT_KEY)); dte.setTracestate(extension.get(TRACE_STATE_KEY)); - + return new Format(dte); }); - - CloudEventBuilder builder = + + CloudEventBuilder builder = CloudEventBuilder.builder(event); - + extensions.get().forEach(extension -> { builder.withExtension(extension); }); - + dteFormat.ifPresent(tracing -> { builder.withExtension(tracing); }); - + return builder.build(); }); } diff --git a/kafka/src/test/java/io/cloudevents/kafka/CloudEventsKafkaHeadersTest.java b/kafka/src/test/java/io/cloudevents/kafka/CloudEventsKafkaHeadersTest.java index 811db9c3c..5ccd20a3f 100644 --- a/kafka/src/test/java/io/cloudevents/kafka/CloudEventsKafkaHeadersTest.java +++ b/kafka/src/test/java/io/cloudevents/kafka/CloudEventsKafkaHeadersTest.java @@ -52,4 +52,4 @@ private void assertHeaders(final Iterable
headers, final Map Date: Mon, 20 Apr 2020 15:59:17 +0200 Subject: [PATCH 11/21] Enable JUnit 5 Signed-off-by: Francesco Guardiani --- api/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/api/pom.xml b/api/pom.xml index 712765d8a..797cd6140 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -69,4 +69,14 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M4 + + + + From 814bf807451d5b40577a5cb8260106b949cf012f Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 16:44:49 +0200 Subject: [PATCH 12/21] Removed JsonNode from Data (now jackson should be easily pluggable) Signed-off-by: Francesco Guardiani --- .../main/java/io/cloudevents/CloudEvent.java | 13 +-- .../cloudevents/DataConversionException.java | 8 -- .../impl/BaseCloudEventBuilder.java | 44 +++------ .../io/cloudevents/impl/CloudEventImpl.java | 87 ++---------------- .../message/MessageVisitException.java | 6 +- .../io/cloudevents/v03/AttributesImpl.java | 2 +- .../io/cloudevents/v1/AttributesImpl.java | 2 +- .../message/EventMessageRoundtripTest.java | 4 +- .../java/io/cloudevents/mock/CSVFormat.java | 92 +++++++++++++++++++ .../test/java/io/cloudevents/test/Data.java | 13 ++- 10 files changed, 130 insertions(+), 141 deletions(-) delete mode 100644 api/src/main/java/io/cloudevents/DataConversionException.java create mode 100644 api/src/test/java/io/cloudevents/mock/CSVFormat.java diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 098563041..4ba0b208a 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -15,7 +15,6 @@ */ package io.cloudevents; -import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.format.EventFormat; import io.cloudevents.message.BinaryMessage; import io.cloudevents.message.StructuredMessage; @@ -38,17 +37,7 @@ public interface CloudEvent { /** * The event data */ - Optional getDataAsString() throws DataConversionException; - - /** - * The event data - */ - Optional getDataAsBytes() throws DataConversionException; - - /** - * The event data - */ - Optional getDataAsJson() throws DataConversionException; + Optional getData(); /** * The event extensions diff --git a/api/src/main/java/io/cloudevents/DataConversionException.java b/api/src/main/java/io/cloudevents/DataConversionException.java deleted file mode 100644 index a21e57fc0..000000000 --- a/api/src/main/java/io/cloudevents/DataConversionException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.cloudevents; - -public class DataConversionException extends RuntimeException { - - public DataConversionException(String from, String to, Throwable cause) { - super("Cannot convert " + from + " data to " + to, cause); - } -} diff --git a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java index a8186a69c..27999acf9 100644 --- a/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/impl/BaseCloudEventBuilder.java @@ -1,6 +1,5 @@ package io.cloudevents.impl; -import com.fasterxml.jackson.databind.JsonNode; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.Extension; @@ -16,7 +15,7 @@ public abstract class BaseCloudEventBuilder extensions; @SuppressWarnings("unchecked") @@ -31,7 +30,7 @@ public BaseCloudEventBuilder(CloudEvent event) { CloudEventImpl ev = (CloudEventImpl) event; this.setAttributes(ev.getAttributes()); - this.data = ev.getRawData(); + this.data = ev.getData().orElse(null); this.extensions = new HashMap<>(ev.getExtensions()); } @@ -46,28 +45,22 @@ public BaseCloudEventBuilder(CloudEvent event) { //TODO builder should accept data as Object and use data codecs (that we need to implement) // to encode data - public B withData(String contentType, String data) { - return withEncodedData(contentType, (Object) data); + public B withData(byte[] data) { + this.data = data; + return this.self; } public B withData(String contentType, byte[] data) { - return withEncodedData(contentType, (Object) data); - } - - public B withData(String contentType, JsonNode data) { - return withEncodedData(contentType, (Object) data); - } - - public B withData(String contentType, URI dataSchema, String data) { - return withEncodeData(contentType, dataSchema, (Object) data); + withDataContentType(contentType); + withData(data); + return this.self; } public B withData(String contentType, URI dataSchema, byte[] data) { - return withEncodeData(contentType, dataSchema, (Object) data); - } - - public B withData(String contentType, URI dataSchema, JsonNode data) { - return withEncodeData(contentType, dataSchema, (Object) data); + withDataContentType(contentType); + withDataSchema(dataSchema); + withData(data); + return this.self; } public B withExtension(String key, String value) { @@ -94,19 +87,6 @@ public CloudEvent build() { return new CloudEventImpl(this.buildAttributes(), data, extensions); } - private B withEncodedData(String contentType, Object data) { - withDataContentType(contentType); - this.data = data; - return self; - } - - private B withEncodeData(String contentType, URI dataSchema, Object data) { - withDataContentType(contentType); - withDataSchema(dataSchema); - this.data = data; - return self; - } - @Override public void setExtension(String name, String value) throws MessageVisitException { this.withExtension(name, value); diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index a391386a9..a64b1067d 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -1,20 +1,14 @@ package io.cloudevents.impl; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; -import io.cloudevents.DataConversionException; import io.cloudevents.format.EventFormat; import io.cloudevents.format.json.CloudEventDeserializer; import io.cloudevents.format.json.CloudEventSerializer; -import io.cloudevents.format.json.JsonFormat; import io.cloudevents.message.*; -import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.*; @JsonSerialize(using = CloudEventSerializer.class) @@ -22,10 +16,10 @@ public final class CloudEventImpl implements CloudEvent, BinaryMessage { private final AttributesInternal attributes; - private final Object data; + private final byte[] data; private final Map extensions; - protected CloudEventImpl(Attributes attributes, Object data, Map extensions) { + protected CloudEventImpl(Attributes attributes, byte[] data, Map extensions) { Objects.requireNonNull(attributes); this.attributes = (AttributesInternal) attributes; this.data = data; @@ -38,72 +32,8 @@ public Attributes getAttributes() { } @Override - public Optional getDataAsString() { - if (data != null) { - if (data instanceof String) { - return Optional.of((String)data); - } - if (data instanceof byte[]) { - return Optional.of(new String((byte[]) this.data, StandardCharsets.UTF_8)); - } - if (data instanceof JsonNode) { - JsonNode d = (JsonNode) this.data; - try { - return Optional.of(JsonFormat.MAPPER.writeValueAsString(data)); - } catch (JsonProcessingException e) { - throw new DataConversionException("JsonNode", "String", e); - } - } - throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); - } - return Optional.empty(); - } - - @Override - public Optional getDataAsBytes() { - if (data != null) { - if (data instanceof String) { - return Optional.of(((String)data).getBytes()); - } - if (data instanceof byte[]) { - return Optional.of((byte[])this.data); - } - if (data instanceof JsonNode) { - JsonNode d = (JsonNode) this.data; - try { - return Optional.of(JsonFormat.MAPPER.writeValueAsBytes(data)); - } catch (JsonProcessingException e) { - throw new DataConversionException("JsonNode", "byte[]", e); - } - } - throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); - } - return Optional.empty(); - } - - @Override - public Optional getDataAsJson() { - if (data != null) { - if (data instanceof String) { - try { - return Optional.of(JsonFormat.MAPPER.readTree((String)data)); - } catch (IOException e) { - throw new DataConversionException("String", "JsonNode", e); - } - } - if (data instanceof byte[]) { - try { - return Optional.of(JsonFormat.MAPPER.readTree((byte[]) data)); - } catch (IOException e) { - throw new DataConversionException("[]byte", "JsonNode", e); - } - } - if (data instanceof JsonNode) { - return Optional.of((JsonNode)this.data); - } - throw new IllegalStateException("CloudEventImpl contains an illegal data of class " + data.getClass().getCanonicalName()); - } - return Optional.empty(); + public Optional getData() { + return Optional.ofNullable(this.data); } @Override @@ -129,10 +59,6 @@ public CloudEvent toV1() { ); } - protected Object getRawData() { - return data; - } - // Message impl public BinaryMessage asBinaryMessage() { @@ -169,9 +95,8 @@ public , V> V visit(BinaryMessageVisitorFactor } } - // TODO to be improved to remove the allocation of useless optional if (this.data != null) { - visitor.setBody(this.getDataAsBytes().get()); + visitor.setBody(this.data); } return visitor.end(); @@ -183,7 +108,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; CloudEventImpl that = (CloudEventImpl) o; return Objects.equals(attributes, that.attributes) && - Objects.equals(data, that.data) && + Arrays.equals(data, that.data) && Objects.equals(extensions, that.extensions); } diff --git a/api/src/main/java/io/cloudevents/message/MessageVisitException.java b/api/src/main/java/io/cloudevents/message/MessageVisitException.java index c0650de01..cb32bc0fb 100644 --- a/api/src/main/java/io/cloudevents/message/MessageVisitException.java +++ b/api/src/main/java/io/cloudevents/message/MessageVisitException.java @@ -43,14 +43,14 @@ public static MessageVisitException newInvalidAttributeName(String attributeName public static MessageVisitException newInvalidAttributeType(String attributeName, Class clazz) { return new MessageVisitException( MessageVisitExceptionKind.INVALID_ATTRIBUTE_TYPE, - "Invalid attribute type for " + attributeName + ": " + clazz.getCanonicalName() + "Invalid attribute type for \"" + attributeName + "\": " + clazz.getCanonicalName() ); } public static MessageVisitException newInvalidAttributeValue(String attributeName, Object value, Throwable cause) { return new MessageVisitException( MessageVisitExceptionKind.INVALID_ATTRIBUTE_VALUE, - "Invalid attribute value for " + attributeName + ": " + value, + "Invalid attribute value for \"" + attributeName + "\": " + value, cause ); } @@ -58,7 +58,7 @@ public static MessageVisitException newInvalidAttributeValue(String attributeNam public static MessageVisitException newInvalidExtensionType(String extensionName, Class clazz) { return new MessageVisitException( MessageVisitExceptionKind.INVALID_EXTENSION_TYPE, - "Invalid extension type for " + extensionName + ": " + clazz.getCanonicalName() + "Invalid extension type for \"" + extensionName + "\": " + clazz.getCanonicalName() ); } } diff --git a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java index 57e18c7cc..017f47cb7 100644 --- a/api/src/main/java/io/cloudevents/v03/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v03/AttributesImpl.java @@ -126,7 +126,7 @@ public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitExc } if (this.time != null) { visitor.setAttribute( - ContextAttributes.TYPE.name().toLowerCase(), + ContextAttributes.TIME.name().toLowerCase(), this.time ); } diff --git a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java index def3bba3b..26129adec 100644 --- a/api/src/main/java/io/cloudevents/v1/AttributesImpl.java +++ b/api/src/main/java/io/cloudevents/v1/AttributesImpl.java @@ -141,7 +141,7 @@ public void visit(BinaryMessageAttributesVisitor visitor) throws MessageVisitExc } if (this.time != null) { visitor.setAttribute( - ContextAttributes.TYPE.name().toLowerCase(), + ContextAttributes.TIME.name().toLowerCase(), this.time ); } diff --git a/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java b/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java index 6e574a10c..c9394675d 100644 --- a/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java +++ b/api/src/test/java/io/cloudevents/message/EventMessageRoundtripTest.java @@ -1,7 +1,7 @@ package io.cloudevents.message; import io.cloudevents.CloudEvent; -import io.cloudevents.format.json.JsonFormat; +import io.cloudevents.mock.CSVFormat; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -12,7 +12,7 @@ public class EventMessageRoundtripTest { @ParameterizedTest() @MethodSource("io.cloudevents.test.Data#allEvents") void structuredToEvent(CloudEvent input) { - assertThat(input.asStructuredMessage(JsonFormat.getInstance()).toEvent()) + assertThat(input.asStructuredMessage(CSVFormat.INSTANCE).toEvent()) .isEqualTo(input); } diff --git a/api/src/test/java/io/cloudevents/mock/CSVFormat.java b/api/src/test/java/io/cloudevents/mock/CSVFormat.java new file mode 100644 index 000000000..b0093d3ed --- /dev/null +++ b/api/src/test/java/io/cloudevents/mock/CSVFormat.java @@ -0,0 +1,92 @@ +package io.cloudevents.mock; + +import io.cloudevents.CloudEvent; +import io.cloudevents.SpecVersion; +import io.cloudevents.format.EventFormat; +import io.cloudevents.types.Time; +import io.cloudevents.v1.CloudEventBuilder; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.util.Base64; +import java.util.Collections; +import java.util.Set; +import java.util.regex.Pattern; + +public class CSVFormat implements EventFormat { + + public static final CSVFormat INSTANCE = new CSVFormat(); + + @Override + public byte[] serializeToBytes(CloudEvent event) { + return serializeToString(event).getBytes(); + } + + @Override + public String serializeToString(CloudEvent event) { + return String.join( + ",", + event.getAttributes().getSpecVersion().toString(), + event.getAttributes().getId(), + event.getAttributes().getType(), + event.getAttributes().getSource().toString(), + event.getAttributes().getDataContentType().orElse("null"), + event.getAttributes().getDataSchema().map(URI::toString).orElse("null"), + event.getAttributes().getSubject().orElse("null"), + event.getAttributes().getTime().map(Time.RFC3339_DATE_FORMAT::format).orElse("null"), + event.getData().map(d -> new String(Base64.getEncoder().encode(d), StandardCharsets.UTF_8)).orElse("null") + ); + } + + @Override + public CloudEvent deserialize(byte[] event) { + return deserialize(new String(event, StandardCharsets.UTF_8)); + } + + @Override + public CloudEvent deserialize(String event) { + String[] splitted = event.split(Pattern.quote(",")); + SpecVersion sv = SpecVersion.parse(splitted[0]); + + String id = splitted[1]; + String type = splitted[2]; + URI source = URI.create(splitted[3]); + String datacontenttype = splitted[4].equals("null") ? null : splitted[4]; + URI dataschema = splitted[5].equals("null") ? null : URI.create(splitted[5]); + String subject = splitted[6].equals("null") ? null : splitted[6]; + ZonedDateTime time = splitted[7].equals("null") ? null : Time.parseTime(splitted[7]); + byte[] data = splitted[8].equals("null") ? null : Base64.getDecoder().decode(splitted[8].getBytes()); + + CloudEventBuilder builder = CloudEvent.buildV1() + .withId(id) + .withType(type) + .withSource(source); + + if (datacontenttype != null) { + builder.withDataContentType(datacontenttype); + } + if (dataschema != null) { + builder.withDataSchema(dataschema); + } + if (subject != null) { + builder.withSubject(subject); + } + if (time != null) { + builder.withTime(time); + } + if (data != null) { + builder.withData(data); + } + switch (sv) { + case V03: return builder.build().toV03(); + case V1: return builder.build().toV1(); + } + return null; + } + + @Override + public Set supportedContentTypes() { + return Collections.singleton("text/csv"); + } +} diff --git a/api/src/test/java/io/cloudevents/test/Data.java b/api/src/test/java/io/cloudevents/test/Data.java index ff305823f..18f3d453a 100644 --- a/api/src/test/java/io/cloudevents/test/Data.java +++ b/api/src/test/java/io/cloudevents/test/Data.java @@ -1,9 +1,11 @@ package io.cloudevents.test; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import io.cloudevents.CloudEvent; +import io.cloudevents.format.json.JsonFormat; import java.net.URI; import java.time.ZonedDateTime; @@ -21,6 +23,15 @@ public class Data { public static final ZonedDateTime TIME = ZonedDateTime.now(); public static final JsonNode DATA_JSON = new ObjectNode(JsonNodeFactory.instance); + public static byte[] DATA_JSON_SERIALIZED; + + static { + try { + DATA_JSON_SERIALIZED = JsonFormat.MAPPER.writeValueAsBytes(DATA_JSON); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } public static final CloudEvent V1_MIN = CloudEvent.buildV1() .withId(ID) @@ -32,7 +43,7 @@ public class Data { .withId(ID) .withType(TYPE) .withSource(SOURCE) - .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON) + .withData(DATACONTENTTYPE_JSON, DATASCHEMA, DATA_JSON_SERIALIZED) .withSubject(SUBJECT) .withTime(TIME) .build(); From 41f48a5c84f6463613fde7375facb59101187d87 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 20 Apr 2020 16:57:58 +0200 Subject: [PATCH 13/21] cloudevents-api does not depend on jackson anymore Signed-off-by: Francesco Guardiani --- api/pom.xml | 10 --- .../format/EventFormatProvider.java | 9 +- .../io/cloudevents/impl/CloudEventImpl.java | 6 -- .../test/java/io/cloudevents/test/Data.java | 16 +--- formats/json-jackson/pom.xml | 89 +++++++++++++++++++ .../format/json/CloudEventDeserializer.java | 0 .../format/json/CloudEventSerializer.java | 0 .../cloudevents/format/json/JsonFormat.java | 0 .../json/ZonedDateTimeDeserializer.java | 0 .../format/json/ZonedDateTimeSerializer.java | 0 .../io.cloudevents.format.EventFormat | 1 + .../format/json/JsonFormatTest.java | 0 pom.xml | 1 + 13 files changed, 98 insertions(+), 34 deletions(-) create mode 100644 formats/json-jackson/pom.xml rename {api => formats/json-jackson}/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java (100%) rename {api => formats/json-jackson}/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java (100%) rename {api => formats/json-jackson}/src/main/java/io/cloudevents/format/json/JsonFormat.java (100%) rename {api => formats/json-jackson}/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java (100%) rename {api => formats/json-jackson}/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java (100%) create mode 100644 formats/json-jackson/src/main/resources/META-INF/services/io.cloudevents.format.EventFormat rename {api => formats/json-jackson}/src/test/java/io/cloudevents/format/json/JsonFormatTest.java (100%) diff --git a/api/pom.xml b/api/pom.xml index 797cd6140..310a30d67 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -42,16 +42,6 @@ - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - org.junit.jupiter diff --git a/api/src/main/java/io/cloudevents/format/EventFormatProvider.java b/api/src/main/java/io/cloudevents/format/EventFormatProvider.java index 8053d9622..c077da005 100644 --- a/api/src/main/java/io/cloudevents/format/EventFormatProvider.java +++ b/api/src/main/java/io/cloudevents/format/EventFormatProvider.java @@ -1,8 +1,8 @@ package io.cloudevents.format; -import io.cloudevents.format.json.JsonFormat; - import java.util.HashMap; +import java.util.ServiceLoader; +import java.util.stream.StreamSupport; public final class EventFormatProvider { @@ -18,7 +18,10 @@ public static EventFormatProvider getInstance() { //TODO register stuff with SPI private EventFormatProvider() { - registerFormat(JsonFormat.getInstance()); + StreamSupport.stream( + ServiceLoader.load(EventFormat.class).spliterator(), + false + ).forEach(this::registerFormat); } public void registerFormat(EventFormat format) { diff --git a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java index a64b1067d..cd4dc3707 100644 --- a/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/CloudEventImpl.java @@ -1,18 +1,12 @@ package io.cloudevents.impl; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.cloudevents.Attributes; import io.cloudevents.CloudEvent; import io.cloudevents.format.EventFormat; -import io.cloudevents.format.json.CloudEventDeserializer; -import io.cloudevents.format.json.CloudEventSerializer; import io.cloudevents.message.*; import java.util.*; -@JsonSerialize(using = CloudEventSerializer.class) -@JsonDeserialize(using = CloudEventDeserializer.class) public final class CloudEventImpl implements CloudEvent, BinaryMessage { private final AttributesInternal attributes; diff --git a/api/src/test/java/io/cloudevents/test/Data.java b/api/src/test/java/io/cloudevents/test/Data.java index 18f3d453a..02e7bdae2 100644 --- a/api/src/test/java/io/cloudevents/test/Data.java +++ b/api/src/test/java/io/cloudevents/test/Data.java @@ -1,11 +1,6 @@ package io.cloudevents.test; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.cloudevents.CloudEvent; -import io.cloudevents.format.json.JsonFormat; import java.net.URI; import java.time.ZonedDateTime; @@ -22,16 +17,7 @@ public class Data { public static final String SUBJECT = "sub"; public static final ZonedDateTime TIME = ZonedDateTime.now(); - public static final JsonNode DATA_JSON = new ObjectNode(JsonNodeFactory.instance); - public static byte[] DATA_JSON_SERIALIZED; - - static { - try { - DATA_JSON_SERIALIZED = JsonFormat.MAPPER.writeValueAsBytes(DATA_JSON); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - } + public static byte[] DATA_JSON_SERIALIZED = "{}".getBytes(); public static final CloudEvent V1_MIN = CloudEvent.buildV1() .withId(ID) diff --git a/formats/json-jackson/pom.xml b/formats/json-jackson/pom.xml new file mode 100644 index 000000000..7fce64929 --- /dev/null +++ b/formats/json-jackson/pom.xml @@ -0,0 +1,89 @@ + + + + 4.0.0 + + + io.cloudevents + cloudevents-parent + 1.3.0 + + + cloudevents-json-jackson + CloudEvents - JSON Jackson + ${parent.version} + jar + + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + + + + + + io.cloudevents + cloudevents-api + ${parent.version} + + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.junit.jupiter + junit-jupiter + 5.4.2 + test + + + + org.assertj + assertj-core + 3.10.0 + test + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M4 + + + + + diff --git a/api/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java b/formats/json-jackson/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java similarity index 100% rename from api/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java rename to formats/json-jackson/src/main/java/io/cloudevents/format/json/CloudEventDeserializer.java diff --git a/api/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java b/formats/json-jackson/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java similarity index 100% rename from api/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java rename to formats/json-jackson/src/main/java/io/cloudevents/format/json/CloudEventSerializer.java diff --git a/api/src/main/java/io/cloudevents/format/json/JsonFormat.java b/formats/json-jackson/src/main/java/io/cloudevents/format/json/JsonFormat.java similarity index 100% rename from api/src/main/java/io/cloudevents/format/json/JsonFormat.java rename to formats/json-jackson/src/main/java/io/cloudevents/format/json/JsonFormat.java diff --git a/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java b/formats/json-jackson/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java similarity index 100% rename from api/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java rename to formats/json-jackson/src/main/java/io/cloudevents/format/json/ZonedDateTimeDeserializer.java diff --git a/api/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java b/formats/json-jackson/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java similarity index 100% rename from api/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java rename to formats/json-jackson/src/main/java/io/cloudevents/format/json/ZonedDateTimeSerializer.java diff --git a/formats/json-jackson/src/main/resources/META-INF/services/io.cloudevents.format.EventFormat b/formats/json-jackson/src/main/resources/META-INF/services/io.cloudevents.format.EventFormat new file mode 100644 index 000000000..5e7c99da2 --- /dev/null +++ b/formats/json-jackson/src/main/resources/META-INF/services/io.cloudevents.format.EventFormat @@ -0,0 +1 @@ +io.cloudevents.format.json.JsonFormat diff --git a/api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java b/formats/json-jackson/src/test/java/io/cloudevents/format/json/JsonFormatTest.java similarity index 100% rename from api/src/test/java/io/cloudevents/format/json/JsonFormatTest.java rename to formats/json-jackson/src/test/java/io/cloudevents/format/json/JsonFormatTest.java diff --git a/pom.xml b/pom.xml index af1984cb6..507f45395 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,7 @@ api + formats/json-jackson