diff --git a/api/src/main/java/io/cloudevents/CloudEvent.java b/api/src/main/java/io/cloudevents/CloudEvent.java index 815ab999..f1a3b4b3 100644 --- a/api/src/main/java/io/cloudevents/CloudEvent.java +++ b/api/src/main/java/io/cloudevents/CloudEvent.java @@ -21,24 +21,11 @@ import java.util.Map; import java.util.Optional; /** - * An abstract event envelope, representing the 0.1 version of the CNCF CloudEvent spec. + * An abstract event envelope, representing the 0.2 version of the CNCF CloudEvent spec. * */ public interface CloudEvent { - // required - String EVENT_TYPE_KEY = "ce-type"; - String SPECVERSION_KEY = "ce-specversion"; - String SOURCE_KEY = "ce-source"; - String EVENT_ID_KEY = "ce-id"; - - // none-required - String CONTENT_TYPE_KEY = "contenttype"; - String DATA__KEY = "data"; - String EVENT_TIME_KEY = "ce-time"; - String SCHEMA_URL_KEY = "ce-schemaurl"; - String HEADER_PREFIX = "ce-x-"; - /** * Type of occurrence which has happened. Often this property is used for routing, observability, policy enforcement, etc. */ diff --git a/api/src/main/java/io/cloudevents/CloudEventBuilder.java b/api/src/main/java/io/cloudevents/CloudEventBuilder.java index 5b78422e..cd736f6f 100644 --- a/api/src/main/java/io/cloudevents/CloudEventBuilder.java +++ b/api/src/main/java/io/cloudevents/CloudEventBuilder.java @@ -28,8 +28,7 @@ import java.util.Map; */ public class CloudEventBuilder { - public static String SPEC_VERSION = "0.2"; - + private String specversion; private String contentType; private String type; private URI source; @@ -38,6 +37,14 @@ public class CloudEventBuilder { private URI schemaURL; private T data; + /** + * The version of the CloudEvents specification which the event uses. + */ + public CloudEventBuilder specVersion(final String specVersion) { + this.specversion = specVersion; + return this; + } + /** * Type of occurrence which has happened. Often this property is used for routing, observability, policy enforcement, etc. */ @@ -101,10 +108,15 @@ public class CloudEventBuilder { */ public CloudEvent build() { + // forcing latest (default) version + if (specversion == null) { + specversion = SpecVersion.DEFAULT.toString(); + } + if (type == null || source == null || id == null) { throw new IllegalArgumentException("please provide all required fields"); } - return new DefaultCloudEventImpl(type, SPEC_VERSION, source, id, time, schemaURL, contentType, data); + return new DefaultCloudEventImpl(type, specversion, source, id, time, schemaURL, contentType, data); } } \ No newline at end of file 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 00000000..1b2236ae --- /dev/null +++ b/api/src/main/java/io/cloudevents/SpecVersion.java @@ -0,0 +1,68 @@ +/** + * 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; + +import java.util.HashMap; +import java.util.Map; + +public enum SpecVersion { + + V_01("0.1"), + V_02("0.2"), + DEFAULT(V_02.toString()); + + private final String version; + + SpecVersion(final String version) { + this.version = version; + } + + @Override + public String toString() { + return version; + } + + public String version() { + return version; + } + + public static SpecVersion fromVersion(final String version) { + if (version == null) + return null; + + final SpecVersion specVersion= VERSION_TO_SPEC.get(version); + + if (specVersion == null) + throw new IllegalArgumentException(); + + return specVersion; + } + + private static final Map VERSION_TO_SPEC = + new HashMap<>(); + + static + { + SpecVersion[] instances = SpecVersion.class.getEnumConstants(); + + for (int i = 0; i < instances.length; i++) + { + VERSION_TO_SPEC.put(instances[i].toString(), instances[i]); + } + } + + +} diff --git a/api/src/main/java/io/cloudevents/http/HttpTransportAttributes.java b/api/src/main/java/io/cloudevents/http/HttpTransportAttributes.java new file mode 100644 index 00000000..b2cded89 --- /dev/null +++ b/api/src/main/java/io/cloudevents/http/HttpTransportAttributes.java @@ -0,0 +1,46 @@ +/** + * 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 io.cloudevents.SpecVersion; + +import static io.cloudevents.SpecVersion.V_01; + +public interface HttpTransportAttributes { + + // required attrs + String typeKey(); + String specVersionKey(); + String sourceKey(); + String idKey(); + + // none-required attrs + String timeKey(); + String schemaUrlKey(); + + static HttpTransportAttributes getHttpAttributesForSpec(final SpecVersion specVersion) { + + switch (specVersion) { + + case V_01: return new V01HttpTransportMappers(); + case V_02: + case DEFAULT: return new V02HttpTransportMappers(); + } + + // you should not be here! + throw new IllegalArgumentException("Could not find proper version"); + } +} diff --git a/api/src/main/java/io/cloudevents/http/V01HttpTransportMappers.java b/api/src/main/java/io/cloudevents/http/V01HttpTransportMappers.java new file mode 100644 index 00000000..d32b3a73 --- /dev/null +++ b/api/src/main/java/io/cloudevents/http/V01HttpTransportMappers.java @@ -0,0 +1,51 @@ +/** + * 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; + +public class V01HttpTransportMappers implements HttpTransportAttributes { + + public static final String SPEC_VERSION_KEY = "ce-cloudEventsVersion"; + + @Override + public String typeKey() { + return "ce-eventType"; + } + + @Override + public String specVersionKey() { + return SPEC_VERSION_KEY; + } + + @Override + public String sourceKey() { + return "ce-source"; + } + + @Override + public String idKey() { + return "ce-eventID"; + } + + @Override + public String timeKey() { + return "ce-eventTime"; + } + + @Override + public String schemaUrlKey() { + return "ce-schemaURL"; + } +} diff --git a/api/src/main/java/io/cloudevents/http/V02HttpTransportMappers.java b/api/src/main/java/io/cloudevents/http/V02HttpTransportMappers.java new file mode 100644 index 00000000..ae97c20e --- /dev/null +++ b/api/src/main/java/io/cloudevents/http/V02HttpTransportMappers.java @@ -0,0 +1,47 @@ +/** + * 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; + +public class V02HttpTransportMappers extends V01HttpTransportMappers { + + public static final String SPEC_VERSION_KEY = "ce-specversion"; + + @Override + public String typeKey() { + return "ce-type"; + } + + @Override + public String specVersionKey() { + return SPEC_VERSION_KEY; + } + + @Override + public String idKey() { + return "ce-id"; + } + + @Override + public String timeKey() { + return "ce-time"; + } + + @Override + public String schemaUrlKey() { + return "ce-schemaurl"; + } + +} diff --git a/api/src/main/java/io/cloudevents/impl/DefaultCloudEventImpl.java b/api/src/main/java/io/cloudevents/impl/DefaultCloudEventImpl.java index 298aaf81..dc7369ba 100644 --- a/api/src/main/java/io/cloudevents/impl/DefaultCloudEventImpl.java +++ b/api/src/main/java/io/cloudevents/impl/DefaultCloudEventImpl.java @@ -15,8 +15,11 @@ */ package io.cloudevents.impl; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.cloudevents.CloudEvent; +import io.cloudevents.SpecVersion; import java.io.Serializable; import java.net.URI; @@ -29,11 +32,12 @@ import java.util.Optional; * * @param generic type of the underlying data field. */ +@JsonIgnoreProperties(value = { "eventTypeVersion", "extensions" }) // was removed from 0.1 public class DefaultCloudEventImpl implements CloudEvent, Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; - private String specversion = "0.2"; + private String specversion; private String type = null; private URI source = null; private String id = null; @@ -99,10 +103,12 @@ public class DefaultCloudEventImpl implements CloudEvent, Serializable { // protected setters, used for (JSON) deserialization + @JsonAlias({"specversion", "cloudEventsVersion"}) void setSpecversion(String specversion) { this.specversion = specversion; } + @JsonAlias({"type", "eventType"}) void setType(String type) { this.type = type; } @@ -111,11 +117,13 @@ public class DefaultCloudEventImpl implements CloudEvent, Serializable { this.source = source; } + @JsonAlias({"id", "eventID"}) void setId(String id) { this.id = id; } @JsonDeserialize(using = ZonedDateTimeDeserializer.class) + @JsonAlias({"time", "eventTime"}) void setTime(ZonedDateTime time) { this.time = time; } diff --git a/api/src/test/java/io/cloudevents/CloudEventBuilderTest.java b/api/src/test/java/io/cloudevents/CloudEventBuilderTest.java index 2d8ac89b..ab6dbde8 100644 --- a/api/src/test/java/io/cloudevents/CloudEventBuilderTest.java +++ b/api/src/test/java/io/cloudevents/CloudEventBuilderTest.java @@ -65,7 +65,7 @@ public class CloudEventBuilderTest { assertThat(simpleKeyValueEvent.getSchemaURL().get()).isEqualTo(schemaUri); assertThat(simpleKeyValueEvent.getType()).isEqualTo(type); assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src); - assertThat(simpleKeyValueEvent.getSepcVersion()).isEqualTo("0.2"); + assertThat(simpleKeyValueEvent.getSepcVersion()).isEqualTo(SpecVersion.DEFAULT.toString()); } @Test @@ -88,7 +88,7 @@ public class CloudEventBuilderTest { assertThat(simpleKeyValueEvent.getId()).isEqualTo(id); assertThat(simpleKeyValueEvent.getType()).isEqualTo(type); assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src); - assertThat(simpleKeyValueEvent.getSepcVersion()).isEqualTo("0.2"); + assertThat(simpleKeyValueEvent.getSepcVersion()).isEqualTo(SpecVersion.DEFAULT.toString()); } @Test @@ -109,6 +109,27 @@ public class CloudEventBuilderTest { assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src); } + @Test + public void test01BuilderWithoutDataAndUrn() { + + // given + final String id = UUID.randomUUID().toString(); + final URI src = URI.create("urn:event:from:myapi/resourse/123"); + final String type = "some.Cloud.Event.Type"; + + // when + final CloudEvent> simpleKeyValueEvent = new CloudEventBuilder() + .specVersion("0.1") + .type(type) + .id(id) + .source(src) + .build(); + // than + assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src); + assertThat(simpleKeyValueEvent.getSepcVersion()).isEqualTo(SpecVersion.V_01.toString()); + + } + @Test public void testBuilderWithoutDataAndURISchema() { diff --git a/api/src/test/java/io/cloudevents/CloudEventJacksonTest.java b/api/src/test/java/io/cloudevents/CloudEventJacksonTest.java index 6cacc04e..649ea6e0 100644 --- a/api/src/test/java/io/cloudevents/CloudEventJacksonTest.java +++ b/api/src/test/java/io/cloudevents/CloudEventJacksonTest.java @@ -28,8 +28,20 @@ import static org.assertj.core.api.Assertions.assertThat; public class CloudEventJacksonTest { @Test - public void testParseAzureJSON() { - CloudEvent> ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("azure.json")); + public void testParseAzure01JSON() { + CloudEvent> ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("01_azure.json")); + assertThat(ce.getSepcVersion()).isEqualTo(SpecVersion.V_01.toString()); + assertAzureCloudEvent(ce); + } + + @Test + public void testParseAzure02JSON() { + CloudEvent> ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("02_azure.json")); + assertThat(ce.getSepcVersion()).isEqualTo(SpecVersion.V_02.toString()); + assertAzureCloudEvent(ce); + } + + private void assertAzureCloudEvent(CloudEvent> ce) { assertThat(ce.getType()).isEqualTo("Microsoft.Storage.BlobCreated"); ce.getData().ifPresent(data -> { @@ -40,15 +52,24 @@ public class CloudEventJacksonTest { assertThat(storageDiagnostics).containsOnlyKeys("batchId"); assertThat(storageDiagnostics.get("batchId")).isEqualTo("ba4fb664-f289-4742-8067-6c859411b066"); }); - - - } @Test - public void testParseAmazonJSON() { - CloudEvent ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("aws.json")); + public void testParseAmazon01JSON() { + CloudEvent ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("01_aws.json")); + assertAmazonCloudEvent(ce); + } + + @Test + public void testParseAmazon02JSON() { + CloudEvent ce = JacksonMapper.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("02_aws.json")); + assertAmazonCloudEvent(ce); + } + + private void assertAmazonCloudEvent(CloudEvent ce) { assertThat(ce.getType()).isEqualTo("aws.s3.object.created"); + assertThat(ce.getId()).isEqualTo("C234-1234-1234"); + assertThat(ce.getData().isPresent()); assertThat(ce.getSource().equals(URI.create("https://serverless.com"))); assertThat(ce.getTime().get()).isEqualTo(ZonedDateTime.parse("2018-04-26T14:48:09.769Z", ISO_ZONED_DATE_TIME)); } diff --git a/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java b/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java new file mode 100644 index 00000000..16d9eadd --- /dev/null +++ b/api/src/test/java/io/cloudevents/http/HttpTransportAttributesTest.java @@ -0,0 +1,52 @@ +/** + * 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 io.cloudevents.SpecVersion; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class HttpTransportAttributesTest { + + @Test + public void testVersion01Headers() { + + final HttpTransportAttributes v01 = HttpTransportAttributes.getHttpAttributesForSpec(SpecVersion.V_01); + assertThat(v01.specVersionKey()).isEqualTo("ce-cloudEventsVersion"); + assertThat(v01.timeKey()).isEqualTo("ce-eventTime"); + assertThat(v01.idKey()).isEqualTo("ce-eventID"); + assertThat(v01.schemaUrlKey()).isEqualTo("ce-schemaURL"); + assertThat(v01.typeKey()).isEqualTo("ce-eventType"); + + // non-changed between 01 / 02 + assertThat(v01.sourceKey()).isEqualTo("ce-source"); + } + + @Test + public void testVersion02Headers() { + + final HttpTransportAttributes v02 = HttpTransportAttributes.getHttpAttributesForSpec(SpecVersion.V_02); + assertThat(v02.specVersionKey()).isEqualTo("ce-specversion"); + assertThat(v02.timeKey()).isEqualTo("ce-time"); + assertThat(v02.idKey()).isEqualTo("ce-id"); + assertThat(v02.schemaUrlKey()).isEqualTo("ce-schemaurl"); + assertThat(v02.typeKey()).isEqualTo("ce-type"); + + // non-changed between 01 / 02 + assertThat(v02.sourceKey()).isEqualTo("ce-source"); + } +} diff --git a/api/src/test/resources/01_aws.json b/api/src/test/resources/01_aws.json new file mode 100644 index 00000000..a9715faf --- /dev/null +++ b/api/src/test/resources/01_aws.json @@ -0,0 +1,24 @@ +{ + "eventType": "aws.s3.object.created", + "eventID": "C234-1234-1234", + "eventTime": "2018-04-26T14:48:09.769Z", + "eventTypeVersion": "2.0", + "source": "https://serverless.com", + "extensions": {}, + "contentType": "application/json", + "cloudEventsVersion": "0.1", + "data": + { "s3SchemaVersion": "1.0", + "configurationId": "cd267a38-30df-400e-9e3d-d0f1ca6e2410", + "bucket": + { "name": "cloudevents", + "ownerIdentity": {}, + "arn": "arn:aws:s3:::cloudevents" }, + "object": + { "key": "dan_kohn.jpg", + "size": 444684, + "eTag": "38b01ff16138d7ca0a0eb3f7a88ff815", + "sequencer": "005AE1E6A9A3D61490" + } + } +} \ No newline at end of file diff --git a/api/src/test/resources/01_azure.json b/api/src/test/resources/01_azure.json new file mode 100644 index 00000000..1c2d291a --- /dev/null +++ b/api/src/test/resources/01_azure.json @@ -0,0 +1,21 @@ +{ + "eventID": "96fb5f0b-001e-0108-6dfe-da6e2806f124", + "eventTime": "2018-04-23T12:28:22.4579346Z", + "eventType": "Microsoft.Storage.BlobCreated", + "cloudEventsVersion": "0.1", + "data": { + "api": "PutBlockList", + "clientRequestId": "a23b4aba-2755-4107-8020-8ba6c54b203d", + "requestId": "96fb5f0b-001e-0108-6dfe-da6e28000000", + "eTag": "0x8D5A915B425AFFD", + "contentType": "image/jpeg", + "contentLength": 2779325, + "blobType": "BlockBlob", + "url": "https://cvtest34.blob.core.windows.net/myfiles/IMG_20180224_0004.jpg", + "sequencer": "000000000000000000000000000000BA00000000003db46c", + "storageDiagnostics": { + "batchId": "ba4fb664-f289-4742-8067-6c859411b066" + } + }, + "source": "/subscriptions/326100e2-f69d-4268-8503-075374f62b6e/resourceGroups/cvtest34/providers/Microsoft.Storage/storageAccounts/cvtest34#/blobServices/default/containers/myfiles/blobs/IMG_20180224_0004.jpg" +} diff --git a/api/src/test/resources/aws.json b/api/src/test/resources/02_aws.json similarity index 100% rename from api/src/test/resources/aws.json rename to api/src/test/resources/02_aws.json diff --git a/api/src/test/resources/azure.json b/api/src/test/resources/02_azure.json similarity index 100% rename from api/src/test/resources/azure.json rename to api/src/test/resources/02_azure.json diff --git a/http/vertx/src/main/java/io/cloudevents/http/vertx/VertxCloudEvents.java b/http/vertx/src/main/java/io/cloudevents/http/vertx/VertxCloudEvents.java index bd5d4338..510f52bd 100644 --- a/http/vertx/src/main/java/io/cloudevents/http/vertx/VertxCloudEvents.java +++ b/http/vertx/src/main/java/io/cloudevents/http/vertx/VertxCloudEvents.java @@ -1,4 +1,4 @@ -/* +/** * Copyright 2018 The CloudEvents Authors * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/http/vertx/src/main/java/io/cloudevents/http/vertx/impl/VertxCloudEventsImpl.java b/http/vertx/src/main/java/io/cloudevents/http/vertx/impl/VertxCloudEventsImpl.java index c789b521..ac67ca49 100644 --- a/http/vertx/src/main/java/io/cloudevents/http/vertx/impl/VertxCloudEventsImpl.java +++ b/http/vertx/src/main/java/io/cloudevents/http/vertx/impl/VertxCloudEventsImpl.java @@ -1,4 +1,4 @@ -/* +/** * Copyright 2018 The CloudEvents Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +17,9 @@ package io.cloudevents.http.vertx.impl; import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventBuilder; +import io.cloudevents.SpecVersion; +import io.cloudevents.http.HttpTransportAttributes; +import io.cloudevents.http.V02HttpTransportMappers; import io.cloudevents.http.vertx.VertxCloudEvents; import io.vertx.core.AsyncResult; import io.vertx.core.Future; @@ -31,13 +34,6 @@ import java.net.URI; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import static io.cloudevents.CloudEvent.SPECVERSION_KEY; -import static io.cloudevents.CloudEvent.EVENT_ID_KEY; -import static io.cloudevents.CloudEvent.EVENT_TIME_KEY; -import static io.cloudevents.CloudEvent.EVENT_TYPE_KEY; -import static io.cloudevents.CloudEvent.SCHEMA_URL_KEY; -import static io.cloudevents.CloudEvent.SOURCE_KEY; - public final class VertxCloudEventsImpl implements VertxCloudEvents { private static String readRequiredHeaderValue(final MultiMap headers, final String headerName) { @@ -58,25 +54,33 @@ public final class VertxCloudEventsImpl implements VertxCloudEvents { final MultiMap headers = request.headers(); final CloudEventBuilder builder = new CloudEventBuilder(); - try { - // just check, no need to set the version - readRequiredHeaderValue(headers, SPECVERSION_KEY); + final HttpTransportAttributes httpTransportKeys; + { + if (headers.contains(V02HttpTransportMappers.SPEC_VERSION_KEY)) { + httpTransportKeys = HttpTransportAttributes.getHttpAttributesForSpec(SpecVersion.V_02); + } else { + httpTransportKeys = HttpTransportAttributes.getHttpAttributesForSpec(SpecVersion.V_01); + } + } + + try { builder // set required values - .type(readRequiredHeaderValue(headers, EVENT_TYPE_KEY)) - .source(URI.create(readRequiredHeaderValue(headers ,SOURCE_KEY))) - .id(readRequiredHeaderValue(headers, EVENT_ID_KEY)) + .specVersion(readRequiredHeaderValue(headers, httpTransportKeys.specVersionKey())) + .type(readRequiredHeaderValue(headers, httpTransportKeys.typeKey())) + .source(URI.create(readRequiredHeaderValue(headers ,httpTransportKeys.sourceKey()))) + .id(readRequiredHeaderValue(headers, httpTransportKeys.idKey())) // set optional values .contentType(headers.get(HttpHeaders.CONTENT_TYPE)); - final String eventTime = headers.get(EVENT_TIME_KEY); + final String eventTime = headers.get(httpTransportKeys.timeKey()); if (eventTime != null) { builder.time(ZonedDateTime.parse(eventTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME)); } - final String schemaURL = headers.get(SCHEMA_URL_KEY); + final String schemaURL = headers.get(httpTransportKeys.schemaUrlKey()); if (schemaURL != null) { builder.schemaURL(URI.create(schemaURL)); } @@ -103,21 +107,23 @@ public final class VertxCloudEventsImpl implements VertxCloudEvents { request.putHeader(HttpHeaders.CONTENT_LENGTH, HttpHeaders.createOptimized("0")); } + HttpTransportAttributes httpTransportAttributes = HttpTransportAttributes.getHttpAttributesForSpec(SpecVersion.fromVersion(ce.getSepcVersion())); + // read required headers request .putHeader(HttpHeaders.CONTENT_TYPE, HttpHeaders.createOptimized("application/json")) - .putHeader(HttpHeaders.createOptimized(SPECVERSION_KEY), HttpHeaders.createOptimized(ce.getSepcVersion())) - .putHeader(HttpHeaders.createOptimized(EVENT_TYPE_KEY), HttpHeaders.createOptimized(ce.getType())) - .putHeader(HttpHeaders.createOptimized(SOURCE_KEY), HttpHeaders.createOptimized(ce.getSource().toString())) - .putHeader(HttpHeaders.createOptimized(EVENT_ID_KEY), HttpHeaders.createOptimized(ce.getId())); + .putHeader(HttpHeaders.createOptimized(httpTransportAttributes.specVersionKey()), HttpHeaders.createOptimized(ce.getSepcVersion())) + .putHeader(HttpHeaders.createOptimized(httpTransportAttributes.typeKey()), HttpHeaders.createOptimized(ce.getType())) + .putHeader(HttpHeaders.createOptimized(httpTransportAttributes.sourceKey()), HttpHeaders.createOptimized(ce.getSource().toString())) + .putHeader(HttpHeaders.createOptimized(httpTransportAttributes.idKey()), HttpHeaders.createOptimized(ce.getId())); // read optional headers ce.getTime().ifPresent(eventTime -> { - request.putHeader(HttpHeaders.createOptimized(EVENT_TIME_KEY), HttpHeaders.createOptimized(eventTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); + request.putHeader(HttpHeaders.createOptimized(httpTransportAttributes.timeKey()), HttpHeaders.createOptimized(eventTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))); }); ce.getSchemaURL().ifPresent(schemaUrl -> { - request.putHeader(HttpHeaders.createOptimized(SCHEMA_URL_KEY), HttpHeaders.createOptimized(schemaUrl.toString())); + request.putHeader(HttpHeaders.createOptimized(httpTransportAttributes.schemaUrlKey()), HttpHeaders.createOptimized(schemaUrl.toString())); }); ce.getData().ifPresent(data -> { diff --git a/http/vertx/src/main/java/io/cloudevents/http/vertx/package-info.java b/http/vertx/src/main/java/io/cloudevents/http/vertx/package-info.java index 1532e4c7..61025c9a 100644 --- a/http/vertx/src/main/java/io/cloudevents/http/vertx/package-info.java +++ b/http/vertx/src/main/java/io/cloudevents/http/vertx/package-info.java @@ -1,4 +1,4 @@ -/* +/** * Copyright 2018 The CloudEvents Authors * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/http/vertx/src/test/java/io/cloudevents/http/vertx/VertxCloudEventsTests.java b/http/vertx/src/test/java/io/cloudevents/http/vertx/VertxCloudEventsTests.java index f0485143..e544be7b 100644 --- a/http/vertx/src/test/java/io/cloudevents/http/vertx/VertxCloudEventsTests.java +++ b/http/vertx/src/test/java/io/cloudevents/http/vertx/VertxCloudEventsTests.java @@ -1,4 +1,4 @@ -/* +/** * Copyright 2018 The CloudEvents Authors * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +17,8 @@ package io.cloudevents.http.vertx; import io.cloudevents.CloudEvent; import io.cloudevents.CloudEventBuilder; +import io.cloudevents.http.V01HttpTransportMappers; +import io.cloudevents.http.V02HttpTransportMappers; import io.cloudevents.http.reactivex.vertx.VertxCloudEvents; import io.vertx.core.http.HttpHeaders; import io.vertx.junit5.Checkpoint; @@ -31,8 +33,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import java.net.URI; import java.util.logging.Logger; -import static io.cloudevents.CloudEvent.SPECVERSION_KEY; -import static io.cloudevents.CloudEvent.EVENT_TYPE_KEY; +import static io.cloudevents.SpecVersion.V_01; +import static io.cloudevents.SpecVersion.V_02; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(VertxExtension.class) @@ -41,12 +43,13 @@ class VertxCloudEventsTests { private final static Logger logger = Logger.getLogger(VertxCloudEventsTests.class.getName()); @Test - @DisplayName("Post a cloud event with a payload") + @DisplayName("Post a 0.2 CloudEvents object with a payload") void cloudEventWithPayload(Vertx vertx, VertxTestContext testContext) { Checkpoint serverCheckpoint = testContext.checkpoint(); Checkpoint clientCheckpoint = testContext.checkpoint(); CloudEvent cloudEvent = new CloudEventBuilder() + .specVersion("0.2") .source(URI.create("http://knative-eventing.com")) .id("foo-bar") .type("pushevent") @@ -80,12 +83,13 @@ class VertxCloudEventsTests { } @Test - @DisplayName("Post a cloud event without a payload") + @DisplayName("Post a 0.2 CloudEvents object without a payload") void cloudEventWithoutPayload(Vertx vertx, VertxTestContext testContext) { Checkpoint serverCheckpoint = testContext.checkpoint(); Checkpoint clientCheckpoint = testContext.checkpoint(); CloudEvent cloudEvent = new CloudEventBuilder() + .specVersion("0.2") .source(URI.create("http://knative-eventing.com")) .id("foo-bar") .type("pushevent") @@ -97,6 +101,14 @@ class VertxCloudEventsTests { .rxReadFromRequest(req) .doOnError(testContext::failNow) .subscribe(event -> testContext.verify(() -> { + + // check headers + assertThat(req.headers().get(V02HttpTransportMappers.SPEC_VERSION_KEY)).isEqualTo(V_02.toString()); + assertThat(req.headers().get(V01HttpTransportMappers.SPEC_VERSION_KEY)).isNull(); + assertThat(req.headers().get("ce-id")).isEqualTo("foo-bar"); + assertThat(req.headers().get("ce-eventID")).isNull(); + + // check parsed object assertThat(event.getId()).isEqualTo(cloudEvent.getId()); assertThat(event.getSource().toString()).isEqualTo(cloudEvent.getSource().toString()); assertThat(event.getType()).isEqualTo(cloudEvent.getType()); @@ -117,6 +129,54 @@ class VertxCloudEventsTests { }); } + @Test + @DisplayName("Post a 0.1 CloudEvents object without a payload") + void cloudEventWithoutPayload01(Vertx vertx, VertxTestContext testContext) { + Checkpoint serverCheckpoint = testContext.checkpoint(); + Checkpoint clientCheckpoint = testContext.checkpoint(); + + CloudEvent cloudEvent = new CloudEventBuilder() + .specVersion("0.1") + .source(URI.create("http://knative-eventing.com")) + .id("foo-bar") + .type("pushevent") + .build(); + + vertx.createHttpServer() + .requestHandler(req -> VertxCloudEvents + .create() + .rxReadFromRequest(req) + .doOnError(testContext::failNow) + .subscribe(event -> testContext.verify(() -> { + + // check headers + assertThat(req.headers().get(V01HttpTransportMappers.SPEC_VERSION_KEY)).isEqualTo(V_01.toString()); + assertThat(req.headers().get(V02HttpTransportMappers.SPEC_VERSION_KEY)).isNull(); + assertThat(req.headers().get("ce-eventID")).isEqualTo("foo-bar"); + assertThat(req.headers().get("ce-id")).isNull(); + + // check parsed object + assertThat(event.getId()).isEqualTo(cloudEvent.getId()); + assertThat(event.getSepcVersion().toString()).isEqualTo(cloudEvent.getSepcVersion()); + assertThat(event.getSource().toString()).isEqualTo(cloudEvent.getSource().toString()); + assertThat(event.getType()).isEqualTo(cloudEvent.getType()); + assertThat(event.getData()).isNotPresent(); + req.response().end(); + serverCheckpoint.flag(); + }))) + .rxListen(8080) + .doOnError(testContext::failNow) + .subscribe(server -> { + HttpClientRequest req = vertx.createHttpClient().post(server.actualPort(), "localhost", "/"); + req.handler(resp -> testContext.verify(() -> { + assertThat(resp.statusCode()).isEqualTo(200); + clientCheckpoint.flag(); + })); + VertxCloudEvents.create().writeToHttpClientRequest(cloudEvent, req); + req.end(); + }); + } + @Test @DisplayName("Post an incomplete cloud event") void incompleteCloudEvent(Vertx vertx, VertxTestContext testContext) { @@ -140,8 +200,9 @@ class VertxCloudEventsTests { .subscribe(server -> { HttpClientRequest req = vertx.createHttpClient().post(server.actualPort(), "localhost", "/"); // create incomplete CloudEvent request - req.putHeader(HttpHeaders.createOptimized(SPECVERSION_KEY), HttpHeaders.createOptimized("0.1")); - req.putHeader(HttpHeaders.createOptimized(EVENT_TYPE_KEY), HttpHeaders.createOptimized("pushevent")); + req.putHeader(HttpHeaders.createOptimized("ce-specversion"), HttpHeaders.createOptimized("0.2")); + req.putHeader(HttpHeaders.createOptimized("ce-type"), HttpHeaders.createOptimized("pushevent")); + req.putHeader(HttpHeaders.createOptimized("foo"), HttpHeaders.createOptimized("bar")); req.putHeader(HttpHeaders.CONTENT_LENGTH, HttpHeaders.createOptimized("0")); req.handler(resp -> testContext.verify(() -> { assertThat(resp.statusCode()).isEqualTo(200);