Merge pull request #49 from fabiojose/master
Kafka binding, spec 0.3 support, preparing for the future
This commit is contained in:
commit
e625f3a33a
|
|
@ -0,0 +1,54 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
TODO
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
### Added
|
||||
- [Attributes](./api/src/main/java/io/cloudevents/Attributes.java) marker
|
||||
interface for context attributes
|
||||
- Support for [Spec v0.3](https://github.com/cloudevents/spec/tree/v0.3)
|
||||
- [ExtensionFormat](./api/src/main/java/io/cloudevents/ExtensionFormat.java)
|
||||
interface for extensions
|
||||
- [HTTP Marshallers](./api/src/main/java/io/cloudevents/v02/http/Marshallers.java) with bare minium marshallers for HTTP Transport Binding
|
||||
- [HTTP Unmarshallers](./api/src/main/java/io/cloudevents/v02/http/Unmarshallers.java) with bare minium unmarshallers for HTTP Transport Binding
|
||||
- [Kafka Marshallers](./kafka/src/main/java/io/cloudevents/v02/kafka/Marshallers.java) with bare minimum marshallers for Kafka Transport Binding
|
||||
- [CloudEventsKafkaProducer](./kafka/src/main/java//io/cloudevents/kafka/CloudEventsKafkaProducer.java) The CloudEvents producer that uses Kafka Clients API
|
||||
- [Kafka Unmarshallers](./kafka/src/main/java/io/cloudevents/v02/kafka/Unmarshallers.java) with bare minium unmarshallers for Kafka Transport Binding
|
||||
- [CloudEventsKafkaConsumer](./kafka/src/main/java//io/cloudevents/kafka/CloudEventsKafkaConsumer.java) The CloudEvents consumer that uses Kafka Clients API
|
||||
- [BinaryMarshaller](./api/src/main/java/io/cloudevents/format/BinaryMarshaller.java) To help in the case of you need to create your own marshallers for binary content mode
|
||||
- [BinaryUnmarshaller](./api/src/main/java/io/cloudevents/format/BinaryUnmarshaller.java) To help in the case of you need to create your own unmarshallers for binary content mode
|
||||
- [StructuredMarshaller](./api/src/main/java/io/cloudevents/format/StructuredMarshaller.java) To help in the case of you need to create your own marshallers for structured content mode
|
||||
- [StructuredUnmarshaller](./api/src/main/java/io/cloudevents/format/StructuredUnmarshaller.java) To help in the case of you need to create your own unmarshallers for structured content mode
|
||||
|
||||
|
||||
### Changed
|
||||
- CloudEvent interface signature, breaking the backward compatibility
|
||||
- Class: `io.cloudevents.v02.CloudEvent` moved to `io.cloudevents.v02.CloudEventImpl`
|
||||
- Class: `io.cloudevents.v02.CloudEventImpl` had changed its signature, breaking the
|
||||
backward compatibility
|
||||
- Method: `io.cloudevents.json.Json.fromInputStream` had moved to Generics signature
|
||||
- Class: `io.cloudevents.http.V02HttpTransportMappers` moved to
|
||||
`io.cloudevents.v02.http.BinaryAttributeMapperImpl` and had changed is signature breaking the backward compatibility
|
||||
|
||||
### Removed
|
||||
- Support for Spec v0.1
|
||||
- Class: `io.cloudevents.impl.DefaultCloudEventImpl`, in favor of a impl for each
|
||||
version
|
||||
- Class: `io.cloudevents.CloudEventBuilder`, in favor of a builder for each version
|
||||
- Enum: `io.cloudevents.SpecVersion`, in favor of specialization of specs
|
||||
- Method: `io.cloudevents.json.Json.decodeCloudEvent`
|
||||
- Class: `io.cloudevents.http.V01HttpTransportMappers` due the unsupported v0.1
|
||||
- interface: `io.cloudevents.http.HttpTransportAttributes`, in favor of the new
|
||||
abstract envelope signature
|
||||
- interface: `io.cloudevents.Extension` in favor of
|
||||
`io.cloudevents.extensions.ExtensionFormat`
|
||||
|
||||
[Unreleased]: https://github.com/cloudevents/sdk-java/compare/v0.3.0...HEAD
|
||||
[0.3.0]: https://github.com/cloudevents/sdk-java/compare/v0.2.1...v0.3.0
|
||||
19
README.md
19
README.md
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
A Java API for the [CloudEvents specification](https://github.com/cloudevents/spec)
|
||||
|
||||
__Checkout the [changelog](./CHANGELOG.md)__
|
||||
|
||||
## Motivation
|
||||
|
||||
The [CloudEvents specification](https://github.com/cloudevents/spec) is a vendor-neutral specification for defining the format of event data that is being exchanged between different cloud systems. The specification basically defines an abstract envelope for any event data payload, without knowing specific implementation details of the actual underlying event. The current version of the spec is at `0.2` and it describes a simple event format, which was demonstrated at [KubeCon 2018](https://youtu.be/TZPPjAv12KU) using different _Serverless platforms_, such as [Apache Openwhisk](https://github.com/apache/incubator-openwhisk).
|
||||
|
|
@ -19,7 +21,7 @@ For Maven based projects, use the following to configure the CloudEvents Java SD
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
<version>0.2.2</version>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
|
@ -27,8 +29,8 @@ Application developers can now create strongly-typed CloudEvents, such as:
|
|||
|
||||
```java
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEvent;
|
||||
import io.cloudevents.v02.ExtensionFormat;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
|
||||
|
|
@ -43,10 +45,11 @@ final DistributedTracingExtension dt = new DistributedTracingExtension();
|
|||
dt.setTraceparent("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
|
||||
dt.setTracestate("rojo=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");
|
||||
|
||||
final ExtensionFormat tracing = new DistributedTracingExtension.InMemory(dt);
|
||||
final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt);
|
||||
|
||||
// passing in the given attributes
|
||||
final CloudEvent<MyCustomEvent> cloudEvent = new CloudEventBuilder<MyCustomEvent>()
|
||||
final CloudEventImpl<MyCustomEvent> cloudEvent =
|
||||
CloudEventBuilder.<MyCustomEvent>builder()
|
||||
.withType(eventType)
|
||||
.withId(eventId)
|
||||
.withSource(src)
|
||||
|
|
@ -58,6 +61,12 @@ final CloudEvent<MyCustomEvent> cloudEvent = new CloudEventBuilder<MyCustomEvent
|
|||
final String json = Json.encode(cloudEvent);
|
||||
```
|
||||
|
||||
There are [other detailed ways](./api/README.md) of how to use the marshallers and unmarshallers with HTTP transport binding.
|
||||
|
||||
## Kafka
|
||||
|
||||
The support for kafka transport binding is available. Read the [documentation and examples](./kafka/README.md) of use.
|
||||
|
||||
## Possible Integrations
|
||||
|
||||
The API is kept simple, for allowing a wide range of possible integrations:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,546 @@
|
|||
# CloudEvents API
|
||||
|
||||
The base classes, interfaces and low-level APIs to use CloudEvents.
|
||||
|
||||
## How to Use
|
||||
|
||||
Here we will see how to use the pre-configure marshallers and unmarshallers.
|
||||
|
||||
### Binary Marshaller
|
||||
|
||||
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.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
import io.cloudevents.v02.http.Marshallers;
|
||||
|
||||
//...
|
||||
|
||||
/*Create a tracing extension*/
|
||||
final DistributedTracingExtension dt =
|
||||
new DistributedTracingExtension();
|
||||
|
||||
dt.setTraceparent("0");
|
||||
dt.setTracestate("congo=4");
|
||||
|
||||
/*Format it as extension format*/
|
||||
final ExtensionFormat tracing =
|
||||
new DistributedTracingExtension.Format(dt);
|
||||
|
||||
/* Build a CloudEvent instance */
|
||||
CloudEventImpl<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withType("com.github.pull.create")
|
||||
.withSource(URI.create("https://github.com/cloudevents/spec/pull"))
|
||||
.withId("A234-1234-1234")
|
||||
.withSchemaurl(URI.create("http://my.br"))
|
||||
.withTime(ZonedDateTime.now())
|
||||
.withContenttype("text/plain")
|
||||
.withData("my-data")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
/* Marshal the event as a Wire instance */
|
||||
Wire<String, String, String> wire =
|
||||
Marshallers.<String>
|
||||
binary()
|
||||
.withEvent(() -> ce)
|
||||
.marshal();
|
||||
|
||||
/*
|
||||
* Use the wire result, getting the headers map
|
||||
* and the actual payload
|
||||
*/
|
||||
wire.getHeaders(); //Map<String, String>
|
||||
wire.getPayload(); //Optional<String> which has the JSON
|
||||
|
||||
// Use in the transport binding: http, kafka, etc ...
|
||||
```
|
||||
|
||||
### Binary Umarshaller
|
||||
|
||||
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.v02.AttributesImpl;
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.http.Unmarshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
/* The HTTP headers example */
|
||||
Map<String, Object> httpHeaders = new HashMap<>();
|
||||
httpHeaders.put("ce-specversion", "0.2");
|
||||
httpHeaders.put("ce-type", "com.github.pull.create");
|
||||
httpHeaders.put("ce-source", "https://github.com/cloudevents/spec/pull");
|
||||
httpHeaders.put("ce-id", "A234-1234-1234");
|
||||
httpHeaders.put("ce-time", "2018-04-05T17:31:00Z");
|
||||
httpHeaders.put("ce-schemaurl", "http://my.br");
|
||||
httpHeaders.put("my-ext", "my-custom extension");
|
||||
httpHeaders.put("traceparent", "0");
|
||||
httpHeaders.put("tracestate", "congo=4");
|
||||
httpHeaders.put("Content-Type", "application/json");
|
||||
|
||||
/* The payload */
|
||||
String myPayload = "{\"foo\" : \"rocks\", \"name\" : \"jocker\"}";
|
||||
|
||||
/* Unmarshals as CloudEvent instance */
|
||||
CloudEvent<AttributesImpl, Map> event =
|
||||
Unmarshallers.binary(Map.class)
|
||||
.withHeaders(() -> httpHeaders)
|
||||
.withPayload(() -> myPayload)
|
||||
.unmarshal();
|
||||
|
||||
/* Use the CloudEvent instance attributes, data and extensions */
|
||||
event.getAttributes();
|
||||
event.getData();
|
||||
event.getExtensions();
|
||||
```
|
||||
|
||||
### Structured Marshaller
|
||||
|
||||
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.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
import io.cloudevents.v02.http.Marshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
final DistributedTracingExtension dt =
|
||||
new DistributedTracingExtension();
|
||||
dt.setTraceparent("0");
|
||||
dt.setTracestate("congo=4");
|
||||
|
||||
final ExtensionFormat tracing =
|
||||
new DistributedTracingExtension.Format(dt);
|
||||
|
||||
final CloudEventImpl<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withType("com.github.pull.create")
|
||||
.withSource(URI.create("https://github.com/cloudevents/spec/pull"))
|
||||
.withId("A234-1234-1234")
|
||||
.withSchemaurl(URI.create("http://my.br"))
|
||||
.withTime(ZonedDateTime.now())
|
||||
.withContenttype("text/plain")
|
||||
.withData("my-data")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
final Wire<String, String, String> wire =
|
||||
Marshallers.<String>structured()
|
||||
.withEvent(() -> ce)
|
||||
.marshal();
|
||||
|
||||
/*
|
||||
* Use the wire result, getting the headers map
|
||||
* and the actual payload
|
||||
*/
|
||||
wire.getHeaders(); //Map<String, String>
|
||||
wire.getPayload(); //Optional<String> which has the JSON
|
||||
|
||||
// Use in the transport binding: http, kafka, etc ...
|
||||
```
|
||||
|
||||
### Structured Unmarshaller
|
||||
|
||||
The high-level API to unmarshal CloudEvents as structured content mode.
|
||||
|
||||
```java
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.v02.AttributesImpl;
|
||||
import io.cloudevents.v02.http.Unmarshallers;
|
||||
|
||||
// . . .
|
||||
|
||||
/* The HTTP Headers */
|
||||
Map<String, Object> httpHeaders = new HashMap<>();
|
||||
httpHeaders.put("Content-Type", "application/cloudevents+json");
|
||||
|
||||
/* Distributed Tracing */
|
||||
httpHeaders.put("traceparent", "0x200");
|
||||
httpHeaders.put("tracestate", "congo=9");
|
||||
|
||||
|
||||
/* JSON Payload */
|
||||
String payload = "{\"data\":{\"wow\":\"yes!\"},\"id\":\"x10\",\"source\":\"/source\",\"specversion\":\"0.2\",\"type\":\"event-type\",\"contenttype\":\"application/json\"}";
|
||||
|
||||
/* Unmarshalling . . . */
|
||||
CloudEvent<AttributesImpl, Map> event =
|
||||
Unmarshallers.structured(Map.class)
|
||||
.withHeaders(() -> httpHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
|
||||
/* Use the event instance */
|
||||
event.getAttributes();
|
||||
event.getExtensions();
|
||||
event.getData();
|
||||
```
|
||||
|
||||
## Low-level (Un)Marshalling
|
||||
|
||||
When you need to process different formats, instead of JSON, or if you have a custom extension implementation. You must develop your own (un)marshallers.
|
||||
|
||||
We provide a way to make it easy, well, or with less pain at all.
|
||||
|
||||
Let us introduce the step builders:
|
||||
|
||||
- [BinaryMarshaller](./src/main/java/io/cloudevents/format/BinaryMarshaller.java)
|
||||
- [StructuredMarshaller](./src/main/java/io/cloudevents/format/StructuredMarshaller.java)
|
||||
- [BinaryUnmarshaller](./src/main/java/io/cloudevents/format/BinaryUnmarshaller.java)
|
||||
- [StructuredUnmarshaller](./src/main/java/io/cloudevents/format/StructuredUnmarshaller.java)
|
||||
|
||||
All of them follow the [step builder pattern](https://java-design-patterns.com/patterns/step-builder/), that helps a lot when you are new in the data (un)marshalling. To support them we have a [bunch of functional interfaces](./src/main/java/io/cloudevents/fun), used by each step.
|
||||
|
||||
### Marshaller
|
||||
|
||||
Well, this is how build marshaller using the low-level API.
|
||||
|
||||
**Binary Marshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* The imports used by the example bellow
|
||||
*/
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.BinaryMarshaller;
|
||||
import io.cloudevents.format.Wire;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.json.types.Much;
|
||||
import io.cloudevents.v02.Accessor;
|
||||
import io.cloudevents.v02.AttributesImpl;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are four
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload that will result of marshalling
|
||||
* - Type 4 -> String........: the type of headers values. String for HTTP, byte[] for Kafka . . .
|
||||
*/
|
||||
EventStep<AttributesImpl, Much, String, String> builder =
|
||||
BinaryMarshaller.<AttributesImpl, Much, String, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. The attributes marshalling
|
||||
* - in this step we must provide an impl able to marshal AttributesImpl into a
|
||||
* Map<String, String>
|
||||
*/
|
||||
.map(AttributesImpl::marshal)
|
||||
|
||||
/*
|
||||
* Step 2. Access the internal list of extensions
|
||||
* - here we must provide an accessor for the internal list of extensions
|
||||
*/
|
||||
.map(Accessor::extensionsOf)
|
||||
|
||||
/*
|
||||
* Step 3. The extensions marshalling
|
||||
* - we must provide an impl able to marshal a Collection<ExtensionFormat> into a
|
||||
* Map<String, String>
|
||||
*/
|
||||
.map(ExtensionFormat::marshal)
|
||||
|
||||
/*
|
||||
* Step 4. Mapping to headers
|
||||
* - provide an impl able to map from attributes and extensions into a
|
||||
* Map<String, String>, the headers of transport binding.
|
||||
*/
|
||||
.map(HeaderMapper::map)
|
||||
|
||||
/*
|
||||
* Step 5. The data marshaller
|
||||
* - provider an impl able to marshal the CloudEvents' data into payload
|
||||
* for transport
|
||||
*/
|
||||
.map(Json.<Much, String>marshaller()::marshal)
|
||||
|
||||
/*
|
||||
* Step 6. The wire builder
|
||||
* - to make easy to get the marshalled paylaod and the headers we
|
||||
* have the Wire
|
||||
* - here must to provide a way to create new instance of Wire
|
||||
* - now we get the EventStep<AttributesImpl, Much, String, String>, a common step
|
||||
* that every marshaller returns
|
||||
* - from here we just call withEvent() and marshal() methods
|
||||
*/
|
||||
.builder(Wire<String, String, String>::new);
|
||||
|
||||
/*
|
||||
* Using the marshaller
|
||||
*/
|
||||
Wire<String, String, String> wire =
|
||||
builder
|
||||
.withEvent(() -> myEvent)
|
||||
.marshal();
|
||||
}
|
||||
```
|
||||
|
||||
**Structured Marshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* The imports used by the example bellow
|
||||
*/
|
||||
import java.util.HashMap;
|
||||
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.format.StructuredMarshaller;
|
||||
import io.cloudevents.format.builder.EventStep;
|
||||
import io.cloudevents.json.Json;
|
||||
import io.cloudevents.json.types.Much;
|
||||
import io.cloudevents.v02.Accessor;
|
||||
import io.cloudevents.v02.AttributesImpl;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are four
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload that will result of marshalling
|
||||
* - Type 4 -> String........: the type of headers values. String for HTTP, byte[] for Kafka . . .
|
||||
*/
|
||||
EventStep<AttributesImpl, Much, String, String> builder =
|
||||
StructuredMarshaller.<AttributesImpl, Much, String, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. Setting the media type for the envelope
|
||||
* - here we must to say the name of media type header and it's value
|
||||
*/
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
|
||||
/*
|
||||
* Step 2. The marshaller for envelope
|
||||
* - we must provide an impl able to marshal the cloudevents envelope
|
||||
*/
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, Much>, String>
|
||||
marshaller().marshal(event, new HashMap<>());
|
||||
})
|
||||
|
||||
/*
|
||||
* Step 3. Access the internal list of extensions
|
||||
* - here we must provide an accessor for the internal list of extensions
|
||||
*/
|
||||
.map(Accessor::extensionsOf)
|
||||
|
||||
/*
|
||||
* Step 4. The extensions marshalling
|
||||
* - we must provide an impl able to marshal a Collection<ExtensionFormat> into a Map<String, String>
|
||||
*/
|
||||
.map(ExtensionFormat::marshal)
|
||||
|
||||
/*
|
||||
* Step 5. Mapping to headers
|
||||
* - provide an impl able to map from attributes and extensions into a Map<String, String>, the headers of transport binding.
|
||||
* - now we get the EventStep<AttributesImpl, Much, String, String>, a common step that every marshaller returns
|
||||
* - from here we just call withEvent() and marshal() methods
|
||||
*/
|
||||
.map(HeaderMapper::map);
|
||||
|
||||
/*
|
||||
* Using the marshaller
|
||||
*/
|
||||
Wire<String, String, String> wire =
|
||||
builder
|
||||
.withEvent(() -> myEvent)
|
||||
.marshal();
|
||||
|
||||
```
|
||||
|
||||
### Unmarshaller
|
||||
|
||||
**Binary Unmarshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* 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.v02.AttributesImpl;
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
|
||||
// . . .
|
||||
|
||||
/*
|
||||
* Step 0. Define the types - there are three
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload used in the unmarshalling
|
||||
*/
|
||||
HeadersStep<AttributesImpl, Much, String> builder =
|
||||
BinaryUnmarshaller.<AttributesImpl, Much, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. Mapping from headers Map to attributes one
|
||||
* - we must provide a mapper able to map from transport headers to a map of attributes
|
||||
* - this like a translation, removing the prefixes or unknow names for example
|
||||
*/
|
||||
.map(AttributeMapper::map)
|
||||
|
||||
/*
|
||||
* Step 2. The attributes ummarshalling
|
||||
* - we must provide an impl able to unmarshal a Map of attributes into
|
||||
* an instance of Attributes
|
||||
*/
|
||||
.map(AttributesImpl::unmarshal)
|
||||
|
||||
/*
|
||||
* Step 3. The data umarshaller
|
||||
* - we may provive more than one unmarshaller per media type
|
||||
* - we must provide an impl able to unmashal the payload into
|
||||
* the actual 'data' type instance
|
||||
*/
|
||||
.map("application/json", Json.umarshaller(Much.class)::unmarshal)
|
||||
|
||||
/*
|
||||
* Step 3'. Another data unmarshaller
|
||||
*/
|
||||
.map("media type", (payload, headers) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
|
||||
/*
|
||||
* When we are ok with data unmarshallers we call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 4. The extension mapping
|
||||
* - we must provider an impl able to map from transport headers to map of extensions
|
||||
* - we may use this for extensions that lives in the transport headers
|
||||
*/
|
||||
.map(ExtensionMapper::map)
|
||||
|
||||
/*
|
||||
* Step 5. The extension unmarshaller
|
||||
* - we must provide an impl able to unmarshal from map of extenstions into
|
||||
* actual ones
|
||||
*/
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
|
||||
/*
|
||||
* Step 5'. Another extension unmarshaller
|
||||
*/
|
||||
.map((extensionsMap) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
|
||||
/*
|
||||
* When we are ok with extensions unmarshallers we call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 6. The CloudEvent builder
|
||||
* - we must provide an impl able to take the extensions, data and attributes
|
||||
* and build CloudEvent instances
|
||||
* - now we get the HeadersStep<AttributesImpl, Much, String>, a common step
|
||||
* that event unmarshaller must returns
|
||||
* - from here we just call withHeaders(), withPayload() and unmarshal()
|
||||
*/
|
||||
.builder(CloudEventBuilder.<Much>builder()::build);
|
||||
|
||||
/*
|
||||
* Using the unmarshaller
|
||||
*/
|
||||
CloudEvent<AttributesImpl, Much> myEvent =
|
||||
builder
|
||||
.withHeaders(() -> transportHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
```
|
||||
|
||||
**Structured Unmarshaller**
|
||||
|
||||
```java
|
||||
/*
|
||||
* Step 0. Define the types - there are three
|
||||
* - Type 1 -> AttributesImpl: the implementation of attributes
|
||||
* - Type 2 -> Much..........: the type CloudEvents' 'data'
|
||||
* - Type 3 -> String........: the type of payload used in the unmarshalling
|
||||
*/
|
||||
HeadersStep<AttributesImpl, Much, String> step =
|
||||
StructuredUnmarshaller.<AttributesImpl, Much, String>
|
||||
builder()
|
||||
/*
|
||||
* Step 1. The extension mapping
|
||||
* - we must provider an impl able to map from transport headers to map of extensions
|
||||
* - we may use this for extensions that lives in the transport headers
|
||||
*/
|
||||
.map(ExtensionMapper::map)
|
||||
|
||||
/*
|
||||
* Step 2. The extension unmarshaller
|
||||
* - we must provide an impl able to unmarshal from map of extenstions into actual ones
|
||||
*/
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
|
||||
/*
|
||||
* Step 2'. When we are ok with extension unmarshallers, call next()
|
||||
*/
|
||||
.next()
|
||||
|
||||
/*
|
||||
* Step 3. Envelope unmarshaller
|
||||
* - we must provide an impl able to unmarshal the envelope into cloudevents
|
||||
* - now we get the HeadersStep<AttributesImpl, Much, String>, a common step that event unmarshaller must returns
|
||||
* - from here we just call withHeaders(), withPayload() and unmarshal()
|
||||
*/
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<Much> event =
|
||||
Json.<CloudEventImpl<Much>>
|
||||
decodeValue(payload, CloudEventImpl.class, Much.class);
|
||||
|
||||
CloudEventBuilder<Much> builder =
|
||||
CloudEventBuilder.<Much>builder(event);
|
||||
|
||||
extensions.get().forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
});
|
||||
|
||||
/*
|
||||
* Using the unmarshaller
|
||||
*/
|
||||
CloudEvent<AttributesImpl, Much> myEvent =
|
||||
step
|
||||
.withHeaders(() -> transportHeaders)
|
||||
.withPayload(() -> payload)
|
||||
.unmarshal();
|
||||
```
|
||||
|
|
@ -20,13 +20,13 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>0.2.2-SNAPSHOT</version>
|
||||
<version>0.3.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-api</artifactId>
|
||||
<name>CloudEvents - API</name>
|
||||
<version>0.2.2-SNAPSHOT</version>
|
||||
<version>0.3.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* 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;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
/**
|
||||
* The marker interface for CloudEvents attributes
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface Attributes {
|
||||
|
||||
/**
|
||||
* A common way to get the media type of CloudEvents 'data';
|
||||
* @return If has a value, it MUST follows the <a href="https://tools.ietf.org/html/rfc2046">RFC2046</a>
|
||||
*/
|
||||
@JsonIgnore
|
||||
Optional<String> getMediaType();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* To build a brand new instance of {@link CloudEvent}
|
||||
*/
|
||||
CloudEvent<A, T> build();
|
||||
|
||||
/**
|
||||
* To build a brand new instance of {@link CloudEvent} with another
|
||||
* type of 'data'
|
||||
* @param <TT> 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'
|
||||
*/
|
||||
<TT> CloudEvent<A, TT> build(CloudEvent<A, T> base, String id, TT newData);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
* 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.
|
||||
|
|
@ -15,64 +15,32 @@
|
|||
*/
|
||||
package io.cloudevents;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.cloudevents.impl.DefaultCloudEventImpl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* An abstract event envelope, representing the 0.2 version of the <a href="https://github.com/cloudevents/spec/blob/master/spec.md">CNCF CloudEvent spec</a>.
|
||||
* An abstract event envelope
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @param <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
@JsonDeserialize(as = DefaultCloudEventImpl.class)
|
||||
public interface CloudEvent<T> {
|
||||
public interface CloudEvent<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* Type of occurrence which has happened. Often this property is used for routing, observability, policy enforcement, etc.
|
||||
*/
|
||||
String getType();
|
||||
|
||||
/**
|
||||
* The version of the CloudEvents specification which the event uses. This enables the interpretation of the context.
|
||||
*/
|
||||
String getSpecVersion();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
URI getSource();
|
||||
|
||||
/**
|
||||
* ID of the event. The semantics of this string are explicitly undefined to ease the implementation of producers. Enables deduplication.
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Timestamp of when the event happened.
|
||||
*/
|
||||
Optional<ZonedDateTime> getTime();
|
||||
|
||||
/**
|
||||
* A link to the schema that the data attribute adheres to.
|
||||
*/
|
||||
Optional<URI> getSchemaURL();
|
||||
|
||||
/**
|
||||
* Describe the data encoding format
|
||||
*/
|
||||
Optional<String> getContentType();
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
Optional<T> getData();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
Optional<List<Extension>> getExtensions();
|
||||
}
|
||||
/**
|
||||
* The event context attributes
|
||||
*/
|
||||
A getAttributes();
|
||||
|
||||
/**
|
||||
* The event data
|
||||
*/
|
||||
Optional<T> getData();
|
||||
|
||||
/**
|
||||
* The event extensions
|
||||
*/
|
||||
Map<String, Object> getExtensions();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,130 +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;
|
||||
|
||||
import io.cloudevents.impl.DefaultCloudEventImpl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Builder class to create a Java Object representing a CloudEvent implementation
|
||||
* @param <T> type of the data field
|
||||
*/
|
||||
public class CloudEventBuilder<T> {
|
||||
|
||||
private String specversion;
|
||||
private String contentType;
|
||||
private String type;
|
||||
private URI source;
|
||||
private String id;
|
||||
private ZonedDateTime time;
|
||||
private URI schemaURL;
|
||||
private T data;
|
||||
private final List<Extension> extensions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The version of the CloudEvents specification which the event uses.
|
||||
*/
|
||||
public CloudEventBuilder<T> 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.
|
||||
*/
|
||||
public CloudEventBuilder<T> type(final String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 CloudEventBuilder<T> source(final URI source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ID of the event. The semantics of this string are explicitly undefined to ease the implementation of producers. Enables deduplication.
|
||||
*/
|
||||
public CloudEventBuilder<T> id(final String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamp of when the event happened.
|
||||
*/
|
||||
public CloudEventBuilder<T> time(final ZonedDateTime time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the schema that the data attribute adheres to.
|
||||
*/
|
||||
public CloudEventBuilder<T> schemaURL(final URI schemaURL) {
|
||||
this.schemaURL = schemaURL;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the data encoding format
|
||||
*/
|
||||
public CloudEventBuilder<T> contentType(final String contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event payload. The payload depends on the type and schemaURL, the payload is encoded into a media format which is specified by the contenttype attribute (e.g. application/json).
|
||||
*/
|
||||
public CloudEventBuilder<T> data(final T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> extension(final Extension extension) {
|
||||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link CloudEvent} with the previously-set configuration.
|
||||
*/
|
||||
public CloudEvent<T> 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<T>(type, specversion, source, id, time, schemaURL, contentType, data, extensions);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +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;
|
||||
|
||||
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<String, SpecVersion> 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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
package io.cloudevents.extensions;
|
||||
|
||||
import io.cloudevents.Extension;
|
||||
import io.cloudevents.v02.ExtensionFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
|
||||
public class DistributedTracingExtension implements Extension {
|
||||
public class DistributedTracingExtension {
|
||||
|
||||
private String traceparent;
|
||||
private String tracestate;
|
||||
|
|
@ -65,8 +68,6 @@ public class DistributedTracingExtension implements Extension {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The in-memory format for distributed tracing.
|
||||
* <br/>
|
||||
|
|
@ -74,24 +75,62 @@ public class DistributedTracingExtension implements Extension {
|
|||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public static class InMemory implements ExtensionFormat {
|
||||
public static class Format implements ExtensionFormat {
|
||||
|
||||
public static final String IN_MEMORY_KEY = "distributedTracing";
|
||||
|
||||
private final Extension extension;
|
||||
public InMemory(DistributedTracingExtension extension) {
|
||||
this.extension = extension;
|
||||
public static final String TRACE_PARENT_KEY = "traceparent";
|
||||
public static final String TRACE_STATE_KEY = "tracestate";
|
||||
|
||||
private final InMemoryFormat memory;
|
||||
private final Map<String, String> 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 String getKey() {
|
||||
return IN_MEMORY_KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Extension getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return transport;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshals the {@link DistributedTracingExtension} based on map of extensions.
|
||||
* @param exts
|
||||
* @return
|
||||
*/
|
||||
public static Optional<ExtensionFormat> unmarshall(
|
||||
Map<String, String> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* 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<String, String> transport();
|
||||
|
||||
public static ExtensionFormat of(final InMemoryFormat inMemory,
|
||||
final String key, final String value) {
|
||||
|
||||
final Map<String, String> transport = new HashMap<>();
|
||||
transport.put(key, value);
|
||||
|
||||
return new ExtensionFormat() {
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return inMemory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return Collections.unmodifiableMap(transport);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static ExtensionFormat of(final InMemoryFormat inMemory,
|
||||
Entry<String, String> ... transport){
|
||||
Objects.requireNonNull(inMemory);
|
||||
Objects.requireNonNull(transport);
|
||||
|
||||
final Map<String, String> transports = Arrays.asList(transport)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
|
||||
return new ExtensionFormat() {
|
||||
@Override
|
||||
public InMemoryFormat memory() {
|
||||
return inMemory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> transport() {
|
||||
return transports;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshals a collection of {@link ExtensionFormat} to {@code Map<String, String>}
|
||||
* @param extensions
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> marshal(Collection<ExtensionFormat>
|
||||
extensions) {
|
||||
|
||||
return extensions.stream()
|
||||
.map(ExtensionFormat::transport)
|
||||
.flatMap(t -> t.entrySet().stream())
|
||||
.collect(Collectors.toMap(Entry::getKey,
|
||||
Entry::getValue));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* 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.
|
||||
* <br>
|
||||
* See details about in-memory format
|
||||
* <a href="https://github.com/cloudevents/spec/blob/v0.2/documented-extensions.md#usage">here</a>
|
||||
* @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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @param <H> The type of headers value
|
||||
* @return
|
||||
*/
|
||||
public static <A extends Attributes, T, P, H>
|
||||
AttributeMarshalStep<A, T, P, H> builder() {
|
||||
|
||||
return new Builder<A, T, P, H>();
|
||||
}
|
||||
|
||||
public static interface AttributeMarshalStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the {@link Attributes} instance into a
|
||||
* {@code Map<String, String>}
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
ExtensionsAccessorStep<A, T, P, H> map(AttributeMarshaller<A> marshaller);
|
||||
}
|
||||
|
||||
public static interface ExtensionsAccessorStep<A extends Attributes, T, P, H> {
|
||||
|
||||
/**
|
||||
* To get access of internal collection of {@link ExtensionFormat}
|
||||
* @param accessor
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor);
|
||||
|
||||
}
|
||||
|
||||
public static interface ExtensionsStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the collection of {@link ExtensionFormat} into a
|
||||
* {@code Map<String, String>}
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
HeaderMapStep<A, T, P, H> map(ExtensionMarshaller marshaller);
|
||||
}
|
||||
|
||||
public static interface HeaderMapStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the map of attributes and extensions into a map of headers
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
DataMarshallerStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
|
||||
}
|
||||
|
||||
public static interface DataMarshallerStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Marshals the 'data' into payload
|
||||
* @param marshaller
|
||||
* @return
|
||||
*/
|
||||
BuilderStep<A, T, P, H> map(DataMarshaller<P, T, H> marshaller);
|
||||
}
|
||||
|
||||
public static interface BuilderStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Builds the {@link Wire} to use for wire transfer
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
EventStep<A, T, P, H> builder(WireBuilder<P, String, H> builder);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P, H> implements
|
||||
AttributeMarshalStep<A, T, P, H>,
|
||||
ExtensionsAccessorStep<A, T, P, H>,
|
||||
ExtensionsStep<A, T, P, H>,
|
||||
DataMarshallerStep<A, T, P, H>,
|
||||
HeaderMapStep<A, T, P, H>,
|
||||
BuilderStep<A, T, P, H>,
|
||||
EventStep<A, T, P, H>,
|
||||
MarshalStep<P, H> {
|
||||
|
||||
private AttributeMarshaller<A> attributeMarshaller;
|
||||
private ExtensionFormatAccessor<A, T> extensionsAccessor;
|
||||
private ExtensionMarshaller extensionMarshaller;
|
||||
private FormatHeaderMapper<H> headerMapper;
|
||||
private DataMarshaller<P, T, H> dataMarshaller;
|
||||
private WireBuilder<P, String, H> wireBuilder;
|
||||
private Supplier<CloudEvent<A, T>> eventSupplier;
|
||||
|
||||
@Override
|
||||
public ExtensionsAccessorStep<A, T, P, H> map(AttributeMarshaller<A> marshaller) {
|
||||
this.attributeMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor) {
|
||||
this.extensionsAccessor = accessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderMapStep<A, T, P, H> map(ExtensionMarshaller marshaller) {
|
||||
this.extensionMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataMarshallerStep<A, T, P, H> map(FormatHeaderMapper<H> mapper) {
|
||||
this.headerMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuilderStep<A, T, P, H> map(DataMarshaller<P, T, H> marshaller) {
|
||||
this.dataMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> builder(WireBuilder<P, String, H> builder) {
|
||||
this.wireBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event) {
|
||||
this.eventSupplier = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wire<P, String, H> marshal() {
|
||||
CloudEvent<A, T> event = eventSupplier.get();
|
||||
|
||||
Map<String, String> attributesMap =
|
||||
attributeMarshaller.marshal(event.getAttributes());
|
||||
|
||||
Collection<ExtensionFormat> extensionsFormat =
|
||||
extensionsAccessor.extensionsOf(event);
|
||||
|
||||
Map<String, String> extensionsMap =
|
||||
extensionMarshaller.marshal(extensionsFormat);
|
||||
|
||||
Map<String, H> headers =
|
||||
headerMapper.map(attributesMap, extensionsMap);
|
||||
|
||||
P payload = null;
|
||||
if(event.getData().isPresent()) {
|
||||
payload = dataMarshaller.marshal(event.getData().get(),
|
||||
headers);
|
||||
}
|
||||
|
||||
return wireBuilder.build(payload, headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
/**
|
||||
* 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.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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @return
|
||||
*/
|
||||
public static <A extends Attributes, T, P> AttributeMapStep<A, T, P>
|
||||
builder() {
|
||||
return new Builder<A, T, P>();
|
||||
}
|
||||
|
||||
public interface AttributeMapStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Maps the map of headers into map of attributes
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
AttributeUmarshallStep<A, T, P> map(BinaryFormatAttributeMapper unmarshaller);
|
||||
}
|
||||
|
||||
public interface AttributeUmarshallStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals the map of attributes into instance of {@link Attributes}
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
DataUnmarshallerStep<A, T, P> map(AttributeUnmarshaller<A> unmarshaller);
|
||||
}
|
||||
|
||||
public interface DataUnmarshallerStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals the payload into actual 'data' type
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
DataUnmarshallerStep<A, T, P> map(String mime, DataUnmarshaller<P, T, A> unmarshaller);
|
||||
|
||||
ExtensionsMapStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public interface ExtensionsMapStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Maps the headers map into map of extensions
|
||||
* @param mapper
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> map(FormatExtensionMapper mapper);
|
||||
}
|
||||
|
||||
public interface ExtensionsStepBegin<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Starts the configuration for extensions unmarshal
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> beginExtensions();
|
||||
}
|
||||
|
||||
public interface ExtensionsStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Unmarshals a extension, based on the map of extensions.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* This is an optional step, because you do not have extensions or
|
||||
* do not want to process them at all.
|
||||
*
|
||||
* @param unmarshaller
|
||||
* @return
|
||||
*/
|
||||
ExtensionsStep<A, T, P> map(ExtensionUmarshaller unmarshaller);
|
||||
|
||||
/**
|
||||
* Ends the configuration for extensions unmarshal
|
||||
* @return
|
||||
*/
|
||||
BuilderStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public interface BuilderStep<A extends Attributes, T, P> {
|
||||
/**
|
||||
* Takes the builder to build {@link CloudEvent} instances
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
HeadersStep<A, T, P> builder(EventBuilder<T, A> builder);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P> implements
|
||||
AttributeMapStep<A, T, P>,
|
||||
AttributeUmarshallStep<A, T, P>,
|
||||
DataUnmarshallerStep<A, T, P>,
|
||||
ExtensionsMapStep<A, T, P>,
|
||||
ExtensionsStep<A, T, P>,
|
||||
BuilderStep<A, T, P>,
|
||||
HeadersStep<A, T, P>,
|
||||
PayloadStep<A, T, P>,
|
||||
UnmarshalStep<A, T>{
|
||||
|
||||
private BinaryFormatAttributeMapper attributeMapper;
|
||||
private AttributeUnmarshaller<A> attributeUnmarshaller;
|
||||
private Map<String, DataUnmarshaller<P, T, A>> dataUnmarshallers =
|
||||
new HashMap<>();
|
||||
private FormatExtensionMapper extensionMapper;
|
||||
private Set<ExtensionUmarshaller> extensionUnmarshallers =
|
||||
new HashSet<>();
|
||||
private EventBuilder<T, A> eventBuilder;
|
||||
private Supplier<Map<String, Object>> headersSupplier;
|
||||
private Supplier<P> payloadSupplier;
|
||||
|
||||
@Override
|
||||
public AttributeUmarshallStep<A, T, P> map(BinaryFormatAttributeMapper mapper) {
|
||||
this.attributeMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataUnmarshallerStep<A, T, P> map(AttributeUnmarshaller<A> unmarshaller) {
|
||||
this.attributeUnmarshaller = unmarshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataUnmarshallerStep<A, T, P> map(String mime, DataUnmarshaller<P, T, A> unmarshaller) {
|
||||
this.dataUnmarshallers.put(mime, unmarshaller);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<A, T, P> next() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P> map(FormatExtensionMapper mapper) {
|
||||
this.extensionMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionsStep<A, T, P> map(ExtensionUmarshaller unmarshaller) {
|
||||
this.extensionUnmarshallers.add(unmarshaller);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeadersStep<A, T, P> builder(EventBuilder<T, A> builder) {
|
||||
this.eventBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadStep<A, T, P> withHeaders(
|
||||
Supplier<Map<String, Object>> headers) {
|
||||
this.headersSupplier = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmarshalStep<A, T> withPayload(Supplier<P> payload) {
|
||||
this.payloadSupplier = payload;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<A, T> unmarshal() {
|
||||
|
||||
Map<String, Object> headers = headersSupplier.get();
|
||||
P payload = payloadSupplier.get();
|
||||
|
||||
Map<String, String> attributesMap = attributeMapper.map(headers);
|
||||
|
||||
A attributes = attributeUnmarshaller.unmarshal(attributesMap);
|
||||
|
||||
T data = attributes.getMediaType()
|
||||
.map((mime) -> {
|
||||
return dataUnmarshallers.get(mime);
|
||||
})
|
||||
.filter((un) -> null != un)
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(payload, attributes))
|
||||
.orElse(null);
|
||||
|
||||
final Map<String, String> extensionsMap =
|
||||
extensionMapper.map(headers);
|
||||
|
||||
List<ExtensionFormat> extensions =
|
||||
extensionUnmarshallers.stream()
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(extensionsMap))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return eventBuilder.build(data, attributes, extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The CloudEvents 'data' type
|
||||
* @param <P> The CloudEvents marshaled envelope type
|
||||
* @param <H> The header type
|
||||
* @return A new builder to build structured mashaller
|
||||
*/
|
||||
public static <A extends Attributes, T, P, H> MediaTypeStep<A, T, P, H>
|
||||
builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
public static interface MediaTypeStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Sets the media type of CloudEvents envelope
|
||||
* @param headerName Example {@code Content-Type} for HTTP
|
||||
* @param mediaType Example: {@code application/cloudevents+json}
|
||||
*/
|
||||
EnvelopeMarshallerStep<A, T, P, H> mime(String headerName, H mediaType);
|
||||
}
|
||||
|
||||
public static interface EnvelopeMarshallerStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* Sets the marshaller for the CloudEvent
|
||||
* @param marshaller
|
||||
*/
|
||||
ExtensionAccessorStep<A, T, P, H> map(EnvelopeMarshaller<A, T, P> marshaller);
|
||||
}
|
||||
|
||||
public static interface ExtensionAccessorStep<A extends Attributes, T, P, H> {
|
||||
/**
|
||||
* To skip the extension special handling
|
||||
*/
|
||||
EventStep<A, T, P, H> skip();
|
||||
ExtensionMarshallerStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor);
|
||||
}
|
||||
|
||||
public static interface ExtensionMarshallerStep<A extends Attributes, T, P, H> {
|
||||
HeaderMapperStep<A, T, P, H> map(ExtensionMarshaller marshaller);
|
||||
}
|
||||
|
||||
public static interface HeaderMapperStep<A extends Attributes, T, P, H> {
|
||||
EventStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P, H> implements
|
||||
MediaTypeStep<A, T, P, H>,
|
||||
EnvelopeMarshallerStep<A, T, P, H>,
|
||||
ExtensionAccessorStep<A, T, P, H>,
|
||||
ExtensionMarshallerStep<A, T, P, H>,
|
||||
HeaderMapperStep<A, T, P, H>,
|
||||
EventStep<A, T, P, H>,
|
||||
MarshalStep<P, H>{
|
||||
|
||||
private static final Map<String, String> NO_ATTRS =
|
||||
new HashMap<>();
|
||||
|
||||
private String headerName;
|
||||
private H mediaType;
|
||||
|
||||
private EnvelopeMarshaller<A, T, P> marshaller;
|
||||
|
||||
private ExtensionFormatAccessor<A, T> extensionAccessor;
|
||||
|
||||
private ExtensionMarshaller extensionMarshaller;
|
||||
|
||||
private FormatHeaderMapper<H> headerMapper;
|
||||
|
||||
private Supplier<CloudEvent<A, T>> event;
|
||||
|
||||
@Override
|
||||
public EnvelopeMarshallerStep<A, T, P, H> mime(String headerName, H mediaType) {
|
||||
Objects.requireNonNull(headerName);
|
||||
Objects.requireNonNull(mediaType);
|
||||
|
||||
this.headerName = headerName;
|
||||
this.mediaType = mediaType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionAccessorStep<A, T, P, H> map(EnvelopeMarshaller<A, T, P> marshaller) {
|
||||
Objects.requireNonNull(marshaller);
|
||||
|
||||
this.marshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> skip() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionMarshallerStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor) {
|
||||
Objects.requireNonNull(accessor);
|
||||
|
||||
this.extensionAccessor = accessor;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderMapperStep<A, T, P, H> map(ExtensionMarshaller marshaller) {
|
||||
Objects.requireNonNull(marshaller);
|
||||
|
||||
this.extensionMarshaller = marshaller;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventStep<A, T, P, H> map(FormatHeaderMapper<H> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
|
||||
this.headerMapper = mapper;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event) {
|
||||
Objects.requireNonNull(event);
|
||||
|
||||
this.event = event;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wire<P, String, H> marshal() {
|
||||
CloudEvent<A, T> ce = event.get();
|
||||
|
||||
P payload = marshaller.marshal(ce);
|
||||
|
||||
Map<String, H> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* 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 <A extends Attributes, T, P> ExtensionMapperStep<A, T, P>
|
||||
builder() {
|
||||
return new Builder<>();
|
||||
}
|
||||
|
||||
public static interface ExtensionMapperStep<A extends Attributes, T, P> {
|
||||
EnvelopeUnmarshallerStep<A, T, P> skip();
|
||||
ExtensionUnmarshallerStep<A, T, P> map(FormatExtensionMapper mapper);
|
||||
}
|
||||
|
||||
public static interface ExtensionUnmarshallerStep<A extends Attributes, T, P> {
|
||||
ExtensionUnmarshallerStep<A, T, P> map(ExtensionUmarshaller unmarshaller);
|
||||
EnvelopeUnmarshallerStep<A, T, P> next();
|
||||
}
|
||||
|
||||
public static interface EnvelopeUnmarshallerStep<A extends Attributes, T, P> {
|
||||
HeadersStep<A, T, P> map(EnvelopeUnmarshaller<A, T, P> unmarshaller);
|
||||
}
|
||||
|
||||
private static final class Builder<A extends Attributes, T, P> implements
|
||||
ExtensionMapperStep<A, T, P>,
|
||||
ExtensionUnmarshallerStep<A, T, P>,
|
||||
EnvelopeUnmarshallerStep<A, T, P>,
|
||||
HeadersStep<A, T, P>,
|
||||
PayloadStep<A, T, P>,
|
||||
UnmarshalStep<A, T>{
|
||||
|
||||
private FormatExtensionMapper extensionMapper;
|
||||
|
||||
private Set<ExtensionUmarshaller> extensionUnmarshallers =
|
||||
new HashSet<>();
|
||||
|
||||
private EnvelopeUnmarshaller<A, T, P> unmarshaller;
|
||||
|
||||
private Supplier<Map<String, Object>> headersSupplier;
|
||||
|
||||
private Supplier<P> payloadSupplier;
|
||||
|
||||
@Override
|
||||
public Builder<A, T, P> next() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvelopeUnmarshallerStep<A, T, P> skip() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionUnmarshallerStep<A, T, P> map(FormatExtensionMapper mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
|
||||
this.extensionMapper = mapper;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionUnmarshallerStep<A, T, P> map(ExtensionUmarshaller unmarshaller) {
|
||||
Objects.requireNonNull(unmarshaller);
|
||||
|
||||
this.extensionUnmarshallers.add(unmarshaller);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeadersStep<A, T, P> map(EnvelopeUnmarshaller<A, T, P> unmarshaller) {
|
||||
Objects.requireNonNull(unmarshaller);
|
||||
|
||||
this.unmarshaller = unmarshaller;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayloadStep<A, T, P> withHeaders(Supplier<Map<String, Object>> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
this.headersSupplier = headers;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnmarshalStep<A, T> withPayload(Supplier<P> payload) {
|
||||
Objects.requireNonNull(payload);
|
||||
|
||||
this.payloadSupplier = payload;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<A, T> unmarshal() {
|
||||
|
||||
Map<String, Object> headers = headersSupplier.get();
|
||||
P payload = payloadSupplier.get();
|
||||
|
||||
final Map<String, String> extensionsMap =
|
||||
Optional.ofNullable(extensionMapper)
|
||||
.map(mapper -> mapper.map(headers))
|
||||
.orElse(new HashMap<>());
|
||||
|
||||
CloudEvent<A, T> result =
|
||||
unmarshaller.unmarshal(payload,
|
||||
() ->
|
||||
extensionUnmarshallers.stream()
|
||||
.map(unmarshaller ->
|
||||
unmarshaller.unmarshal(extensionsMap))
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* 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 <T> The payload type
|
||||
* @param <K> The header key type
|
||||
* @param <V> The header value type
|
||||
*/
|
||||
public class Wire<T, K, V> {
|
||||
|
||||
private final T payload;
|
||||
private final Map<K, V> headers;
|
||||
|
||||
public Wire(T payload, Map<K, V> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
this.payload = payload;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* The payload
|
||||
*/
|
||||
public Optional<T> getPayload() {
|
||||
return Optional.ofNullable(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* The headers
|
||||
*/
|
||||
public Map<K, V> getHeaders() {
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
* @param <H> The headers value type
|
||||
*/
|
||||
public interface EventStep<A extends Attributes, T, P, H> {
|
||||
|
||||
/**
|
||||
* Supplies the {@link CloudEvent} instance which will be marshaled
|
||||
* @param event cloud event to marshal
|
||||
* @return The next step of builder
|
||||
*/
|
||||
MarshalStep<P, H> withEvent(Supplier<CloudEvent<A, T>> event);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
*/
|
||||
public interface HeadersStep<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Supplies a map of headers to be used by the unmarshaller
|
||||
* @param headers
|
||||
* @return The next step of builder
|
||||
*/
|
||||
PayloadStep<A, T, P> withHeaders(Supplier<Map<String, Object>> headers);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* 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 <P> The payload type
|
||||
* @param <H> The headers value type
|
||||
*/
|
||||
public interface MarshalStep<P, H> {
|
||||
|
||||
/**
|
||||
* Marshals the {@link CloudEvent} instance as {@link Wire}
|
||||
* @return The wire to use in the transports bindings
|
||||
*/
|
||||
Wire<P, String, H> marshal();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
* @param <P> The payload type
|
||||
*/
|
||||
public interface PayloadStep<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Supplies the payload that will be used by the unmarshaller
|
||||
* @param payload
|
||||
* @return The next step o builder
|
||||
*/
|
||||
UnmarshalStep<A, T> withPayload(Supplier<P> payload);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* 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 <A> The attributes type
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public interface UnmarshalStep<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* Unmarshals the payload and headers to {@link CloudEvent} instance
|
||||
* @return New cloud event instance
|
||||
*/
|
||||
CloudEvent<A, T> unmarshal();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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<A extends Attributes> {
|
||||
|
||||
/**
|
||||
* Marshals the {@link Attributes} to map of attributes.
|
||||
*/
|
||||
Map<String, String> marshal(A attributes);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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<A extends Attributes> {
|
||||
|
||||
/**
|
||||
* Unmarshals the map of CloudEvent attributes into actual ones
|
||||
*/
|
||||
A unmarshal(Map<String, String> attributes);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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<String, String> map(Map<String, Object> headers);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* 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 <P> The payload type
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> The type of headers value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DataMarshaller<P, T, H> {
|
||||
|
||||
/**
|
||||
* 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<String, H> headers) throws RuntimeException;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* 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 <P> The payload type
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DataUnmarshaller<P, T, A extends Attributes> {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* 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<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* Marshals a CloudEvent instance into the wire format
|
||||
* @param event The CloudEvents instance
|
||||
* @return the wire format
|
||||
*/
|
||||
P marshal(CloudEvent<A, T> event);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* 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<A extends Attributes, T, P> {
|
||||
|
||||
/**
|
||||
* 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<A, T> unmarshal(P payload,
|
||||
Supplier<List<ExtensionFormat>> extensions);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* 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<T, A extends Attributes> {
|
||||
|
||||
/**
|
||||
* Builds a new event using 'data', 'attributes' and 'extensions'
|
||||
*/
|
||||
CloudEvent<A, T> build(T data, A attributes,
|
||||
Collection<ExtensionFormat> extensions);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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<A extends Attributes, T> {
|
||||
|
||||
/**
|
||||
* To get access to the internal collection of {@link ExtensionFormat} inside
|
||||
* the {@link CloudEvent} implementation
|
||||
*
|
||||
* @param cloudEvent
|
||||
* @return
|
||||
*/
|
||||
Collection<ExtensionFormat> extensionsOf(CloudEvent<A, T> cloudEvent);
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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<String, String> marshal(Collection<ExtensionFormat> extensions);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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<ExtensionFormat> unmarshal(Map<String, String> extensions);
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* Copyright 2018 The CloudEvents Authors
|
||||
* 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.
|
||||
|
|
@ -13,7 +13,22 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.cloudevents;
|
||||
package io.cloudevents.fun;
|
||||
|
||||
public interface Extension {
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FormatExtensionMapper {
|
||||
|
||||
/**
|
||||
* Maps the 'headers' of binary format into extensions
|
||||
* @param headers
|
||||
*/
|
||||
Map<String, String> map(Map<String, Object> headers);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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 <H> The header value type
|
||||
*
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FormatHeaderMapper<H> {
|
||||
|
||||
/**
|
||||
* Maps the 'attributes' and 'extensions' of CloudEvent envelop to
|
||||
* 'headers' of binary format
|
||||
* @param attributes
|
||||
* @return
|
||||
*/
|
||||
Map<String, H> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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<P, K, V> {
|
||||
|
||||
/**
|
||||
* Builds a wire format
|
||||
* @param payload
|
||||
* @param headers
|
||||
* @return
|
||||
*/
|
||||
Wire<P, K, V> build(P payload, Map<K, V> headers);
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +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 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");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +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;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +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;
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,165 +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.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.Extension;
|
||||
import io.cloudevents.json.ZonedDateTimeDeserializer;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Default Java implementation of the CloudEvent API.
|
||||
*
|
||||
* @param <T> generic type of the underlying data field.
|
||||
*/
|
||||
@JsonIgnoreProperties(value = { "eventTypeVersion", "extensions" }) // was removed from 0.1
|
||||
public class DefaultCloudEventImpl<T> implements CloudEvent<T>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
private String specversion;
|
||||
private String type = null;
|
||||
private URI source = null;
|
||||
private String id = null;
|
||||
private ZonedDateTime time = null;
|
||||
private URI schemaURL = null;
|
||||
private String contentType = null;
|
||||
private T data = null;
|
||||
private List<Extension> extensions = null;
|
||||
|
||||
public DefaultCloudEventImpl(final String type, final String specversion, final URI source, final String id, final ZonedDateTime time, final URI schemaURL, final String contentType, final T data, final List<Extension> extensions) {
|
||||
this.specversion = specversion;
|
||||
this.type = type;
|
||||
this.source = source;
|
||||
this.id = id;
|
||||
this.time = time;
|
||||
this.schemaURL = schemaURL;
|
||||
this.contentType = contentType;
|
||||
this.data = data;
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
DefaultCloudEventImpl() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSpecVersion() {
|
||||
return specversion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<URI> getSchemaURL() {
|
||||
return Optional.ofNullable(schemaURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getContentType() {
|
||||
return Optional.ofNullable(contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<Extension>> getExtensions() {
|
||||
return Optional.ofNullable(extensions);
|
||||
}
|
||||
|
||||
// protected setters, used for (JSON) deserialization
|
||||
|
||||
@JsonAlias({"specversion", "specVersion", "cloudEventsVersion"})
|
||||
void setSpecversion(String specversion) {
|
||||
this.specversion = specversion;
|
||||
}
|
||||
|
||||
@JsonAlias({"type", "eventType"})
|
||||
void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
void setSource(URI source) {
|
||||
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;
|
||||
}
|
||||
|
||||
void setSchemaURL(URI schemaURL) {
|
||||
this.schemaURL = schemaURL;
|
||||
}
|
||||
|
||||
@JsonAlias("contenttype")
|
||||
void setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultCloudEventImpl{" +
|
||||
"specversion='" + specversion + '\'' +
|
||||
", type='" + type + '\'' +
|
||||
", source=" + source +
|
||||
", id='" + id + '\'' +
|
||||
", time=" + time +
|
||||
", schemaURL=" + schemaURL +
|
||||
", contentType='" + contentType + '\'' +
|
||||
", data=" + data +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -15,15 +15,20 @@
|
|||
*/
|
||||
package io.cloudevents.json;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.impl.DefaultCloudEventImpl;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.time.ZonedDateTime;
|
||||
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.fun.DataMarshaller;
|
||||
import io.cloudevents.fun.DataUnmarshaller;
|
||||
|
||||
public final class Json {
|
||||
|
||||
|
|
@ -54,11 +59,19 @@ public final class Json {
|
|||
throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static CloudEvent fromInputStream(final InputStream inputStream) {
|
||||
|
||||
/**
|
||||
* 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.readValue(inputStream, DefaultCloudEventImpl.class);
|
||||
return MAPPER.writeValueAsBytes(obj);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalStateException("Failed to encode as JSON: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
@ -73,32 +86,37 @@ public final class Json {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a given JSON string to a CloudEvent .
|
||||
*
|
||||
* @param str the JSON string.
|
||||
* @return an instance of CloudEvent
|
||||
* @throws IllegalStateException when there is a parsing or invalid mapping.
|
||||
*/
|
||||
public static DefaultCloudEventImpl decodeCloudEvent(final String str) throws IllegalStateException {
|
||||
return decodeValue(str, DefaultCloudEventImpl.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <T> the generic type.
|
||||
* @return an instance of T
|
||||
* @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> T decodeValue(final String str, final Class<T> clazz) throws IllegalStateException {
|
||||
try {
|
||||
return MAPPER.readValue(str, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage());
|
||||
}
|
||||
|
||||
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> T binaryDecodeValue(byte[] payload, final Class<T> clazz) {
|
||||
if(null!= payload) {
|
||||
try {
|
||||
return MAPPER.readValue(payload, clazz);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -107,15 +125,148 @@ public final class Json {
|
|||
* @param str the JSON string.
|
||||
* @param type the type to map to.
|
||||
* @param <T> the generic type.
|
||||
* @return an instance of T
|
||||
* @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> T decodeValue(final String str, final TypeReference<T> type) throws IllegalStateException {
|
||||
try {
|
||||
return MAPPER.readValue(str, type);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to decode: " + e.getMessage(), e);
|
||||
}
|
||||
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:
|
||||
* <pre>
|
||||
* String someJson = "...";
|
||||
* Class<?> clazz = Much.class;
|
||||
*
|
||||
* Json.decodeValue(someJson, CloudEventImpl.class, clazz);
|
||||
* </pre>
|
||||
* @param str The JSON String to parse
|
||||
* @param parametrized Actual full type
|
||||
* @param parameterClasses Type parameters to apply
|
||||
* @param <T> 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> 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:
|
||||
* <pre>
|
||||
* String someJson = "...";
|
||||
* Class<?> clazz = Much.class;
|
||||
*
|
||||
* Json.decodeValue(someJson, CloudEventImpl.class, clazz);
|
||||
* </pre>
|
||||
* @param json The JSON byte array to parse
|
||||
* @param parametrized Actual full type
|
||||
* @param parameterClasses Type parameters to apply
|
||||
* @param <T> 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> 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 <T> The 'data' type
|
||||
* @param <A> The attributes type
|
||||
* @param type The type of 'data'
|
||||
* @return A new instance of {@link DataUnmarshaller}
|
||||
*/
|
||||
public static <T, A extends Attributes> DataUnmarshaller<String, T, A>
|
||||
umarshaller(Class<T> type) {
|
||||
return new DataUnmarshaller<String, T, A>() {
|
||||
@Override
|
||||
public T unmarshal(String payload, A attributes) {
|
||||
return Json.decodeValue(payload, type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmarshals a byte array into T type
|
||||
* @param <T> The 'data' type
|
||||
* @param <A> The attributes type
|
||||
* @param payload The byte array
|
||||
* @param attribues
|
||||
* @return The data objects
|
||||
*/
|
||||
public static <T, A extends Attributes> DataUnmarshaller<byte[], T, A>
|
||||
binaryUmarshaller(Class<T> type) {
|
||||
|
||||
return new DataUnmarshaller<byte[], T, A>() {
|
||||
@Override
|
||||
public T unmarshal(byte[] payload, A attributes) {
|
||||
return Json.binaryDecodeValue(payload, type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON Data Marshaller that produces a {@link String}
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> The type of headers value
|
||||
* @return A new instance of {@link DataMarshaller}
|
||||
*/
|
||||
public static <T, H> DataMarshaller<String, T, H> marshaller() {
|
||||
return new DataMarshaller<String, T, H>() {
|
||||
@Override
|
||||
public String marshal(T data, Map<String, H> headers) {
|
||||
return Json.encode(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marshalls the 'data' value as JSON, producing a byte array
|
||||
* @param <T> The 'data' type
|
||||
* @param <H> 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 <T, H> byte[] binaryMarshal(T data,
|
||||
Map<String, H> headers) {
|
||||
return Json.binaryEncode(data);
|
||||
}
|
||||
|
||||
private Json() {
|
||||
|
|
|
|||
|
|
@ -25,8 +25,9 @@ import com.fasterxml.jackson.databind.DeserializationContext;
|
|||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
|
||||
public class ZonedDateTimeDeserializer extends StdDeserializer<ZonedDateTime> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ZonedDateTimeDeserializer() {
|
||||
public ZonedDateTimeDeserializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* 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.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* 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 <A extends Attributes, T> Collection<ExtensionFormat>
|
||||
extensionsOf(CloudEvent<A, T> cloudEvent) {
|
||||
Objects.requireNonNull(cloudEvent);
|
||||
|
||||
if(cloudEvent instanceof CloudEventImpl) {
|
||||
CloudEventImpl impl = (CloudEventImpl)cloudEvent;
|
||||
return impl.getExtensionsFormats();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid instance type: "
|
||||
+ cloudEvent.getClass());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
/**
|
||||
* 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<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* A link to the schema that the data attribute adheres to.
|
||||
*/
|
||||
public Optional<URI> getSchemaurl() {
|
||||
return Optional.ofNullable(schemaurl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the data encoding format
|
||||
*/
|
||||
public Optional<String> getContenttype() {
|
||||
return Optional.ofNullable(contenttype);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Optional<String> 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<String, String> 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(schema -> URI.create(schema))
|
||||
.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<String, String> marshal(AttributesImpl attributes) {
|
||||
Objects.requireNonNull(attributes);
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
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.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
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.json.ZonedDateTimeDeserializer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* Implemented using immutable data structure.
|
||||
*
|
||||
*/
|
||||
@JsonInclude(value = Include.NON_ABSENT)
|
||||
public class CloudEvent<T> {
|
||||
|
||||
@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;
|
||||
|
||||
private final T data;
|
||||
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
CloudEvent(String id, URI source, String specversion, String type,
|
||||
ZonedDateTime time, URI schemaurl, String contenttype,
|
||||
T data, Set<ExtensionFormat> extensions) {
|
||||
|
||||
this.id = id;
|
||||
this.source = source;
|
||||
this.specversion = specversion;
|
||||
this.type = type;
|
||||
|
||||
this.time = time;
|
||||
this.schemaurl = schemaurl;
|
||||
this.contenttype = contenttype;
|
||||
|
||||
this.data = data;
|
||||
|
||||
this.extensions = extensions
|
||||
.stream()
|
||||
.collect(Collectors
|
||||
.toMap(ExtensionFormat::getKey,
|
||||
ExtensionFormat::getExtension));
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getSpecversion() {
|
||||
return specversion;
|
||||
}
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@JsonDeserialize(using = ZonedDateTimeDeserializer.class)
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
public Optional<URI> getSchemaurl() {
|
||||
return Optional.ofNullable(schemaurl);
|
||||
}
|
||||
public Optional<String> getContenttype() {
|
||||
return Optional.ofNullable(contenttype);
|
||||
}
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
Map<String, Object> getExtensions() {
|
||||
return Collections.unmodifiableMap(extensions);
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
void addExtension(String name, Object value) {
|
||||
extensions.put(name, value);
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static <T> CloudEvent<T> 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,
|
||||
@JsonProperty("data") T data) {
|
||||
|
||||
return new CloudEventBuilder<T>()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withSchemaurl(schemaurl)
|
||||
.withContenttype(contenttype)
|
||||
.withData(data)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,27 @@
|
|||
/**
|
||||
* 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.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;
|
||||
|
|
@ -13,13 +30,22 @@ 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;
|
||||
|
||||
/**
|
||||
* CloudEvent instances builder
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
* @version 0.2
|
||||
*/
|
||||
public class CloudEventBuilder<T> {
|
||||
public class CloudEventBuilder<T> implements EventBuilder<T, AttributesImpl>,
|
||||
Builder<AttributesImpl, T> {
|
||||
private CloudEventBuilder() {}
|
||||
|
||||
private static Validator VALIDATOR;
|
||||
|
||||
private static final String SPEC_VERSION = "0.2";
|
||||
private static final String MESSAGE_SEPARATOR = ", ";
|
||||
|
|
@ -37,23 +63,125 @@ public class CloudEventBuilder<T> {
|
|||
|
||||
private final Set<ExtensionFormat> extensions = new HashSet<>();
|
||||
|
||||
private Validator getValidator() {
|
||||
return Validation.buildDefaultValidatorFactory().getValidator();
|
||||
private static Validator getValidator() {
|
||||
if(null== VALIDATOR) {
|
||||
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
return VALIDATOR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a brand new builder instance
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder() {
|
||||
return new CloudEventBuilder<T>();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return An new {@link CloudEvent} immutable instance
|
||||
* @param <T> The 'data' type
|
||||
* @param base A base event to copy {@link CloudEvent#getAttributes()},
|
||||
* {@link CloudEvent#getData()} and {@link CloudEvent#getExtensions()}
|
||||
* @return
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder(
|
||||
final CloudEvent<AttributesImpl, T> base) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
CloudEventBuilder<T> result = new CloudEventBuilder<>();
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
result
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent((time) -> {
|
||||
result.withTime(time);
|
||||
});
|
||||
|
||||
attributes.getSchemaurl().ifPresent((schema) -> {
|
||||
result.withSchemaurl(schema);
|
||||
});
|
||||
|
||||
attributes.getContenttype().ifPresent(contenttype -> {
|
||||
result.withContenttype(contenttype);
|
||||
});
|
||||
|
||||
Accessor.extensionsOf(base)
|
||||
.forEach(extension -> {
|
||||
result.withExtension(extension);
|
||||
});
|
||||
|
||||
base.getData().ifPresent(data -> {
|
||||
result.withData(data);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param <T> 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 CloudEvent<T> build() {
|
||||
CloudEvent<T> event = new CloudEvent<>(id, source, SPEC_VERSION, type,
|
||||
time, schemaurl, contenttype, data, extensions);
|
||||
public static <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions) {
|
||||
CloudEventBuilder<T> builder = new CloudEventBuilder<T>()
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
Set<ConstraintViolation<CloudEvent<T>>> violations =
|
||||
attributes.getTime().ifPresent((time) -> {
|
||||
builder.withTime(time);
|
||||
});
|
||||
|
||||
attributes.getSchemaurl().ifPresent((schema) -> {
|
||||
builder.withSchemaurl(schema);
|
||||
});
|
||||
|
||||
attributes.getContenttype().ifPresent(contenttype -> {
|
||||
builder.withContenttype(contenttype);
|
||||
});
|
||||
|
||||
extensions.stream()
|
||||
.forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.withData(data).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions){
|
||||
return CloudEventBuilder.<T>of(data, attributes, extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return An new {@link CloudEventImpl} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
@Override
|
||||
public CloudEventImpl<T> build() {
|
||||
AttributesImpl attributes = new AttributesImpl(type, SPEC_VERSION,
|
||||
source, id, time, schemaurl, contenttype);
|
||||
|
||||
CloudEventImpl<T> event = new CloudEventImpl<>(attributes, data, extensions);
|
||||
|
||||
Set<ConstraintViolation<Object>> violations =
|
||||
getValidator().validate(event);
|
||||
|
||||
violations.addAll(getValidator().validate(event.getAttributes()));
|
||||
|
||||
final String errs =
|
||||
violations.stream()
|
||||
.map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage()))
|
||||
|
|
@ -108,4 +236,41 @@ public class CloudEventBuilder<T> {
|
|||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <TT> CloudEvent<AttributesImpl, TT>
|
||||
build(CloudEvent<AttributesImpl, T> base, String id, TT newData) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
CloudEventBuilder<TT> builder = new CloudEventBuilder<TT>()
|
||||
.withId(id)
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent((time) -> {
|
||||
builder.withTime(time);
|
||||
});
|
||||
|
||||
attributes.getSchemaurl().ifPresent((schema) -> {
|
||||
builder.withSchemaurl(schema);
|
||||
});
|
||||
|
||||
attributes.getContenttype().ifPresent(contenttype -> {
|
||||
builder.withContenttype(contenttype);
|
||||
});
|
||||
|
||||
Collection<ExtensionFormat> extensions = Accessor.extensionsOf(base);
|
||||
|
||||
extensions.stream()
|
||||
.forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.withData(newData).build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* 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<T> implements CloudEvent<AttributesImpl, T> {
|
||||
|
||||
@JsonIgnore
|
||||
@NotNull
|
||||
private final AttributesImpl attributes;
|
||||
|
||||
private final T data;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private final Set<ExtensionFormat> extensionsFormats;
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, T data,
|
||||
Set<ExtensionFormat> 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<ExtensionFormat> getExtensionsFormats() {
|
||||
return extensionsFormats;
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
@Override
|
||||
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).
|
||||
*/
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> 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 <T> CloudEventImpl<T> 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,
|
||||
@JsonProperty("data") T data) {
|
||||
|
||||
return CloudEventBuilder.<T>builder()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withSchemaurl(schemaurl)
|
||||
.withContenttype(contenttype)
|
||||
.withData(data)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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<String> VALUES =
|
||||
Arrays.asList(ContextAttributes.values())
|
||||
.stream()
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package io.cloudevents.v02;
|
||||
|
||||
import io.cloudevents.Extension;
|
||||
|
||||
/**
|
||||
* See details about in-memory format
|
||||
* <a href="https://github.com/cloudevents/spec/blob/v0.2/documented-extensions.md#usage">here</a>
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public interface ExtensionFormat {
|
||||
/**
|
||||
* The in-memory format key
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
/**
|
||||
* The extension implementation
|
||||
*/
|
||||
Extension getExtension();
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* 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<String, String> map(final Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
final AtomicReference<Optional<Entry<String, Object>>> ct =
|
||||
new AtomicReference<>();
|
||||
|
||||
ct.set(Optional.empty());
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* 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<String> 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<String, String> map(Map<String, Object> 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 -> {
|
||||
return !RESERVED_HEADERS.contains(header.getKey());
|
||||
})
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* 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<String, String> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Objects.requireNonNull(extensions);
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* 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<String, String> NO_HEADERS =
|
||||
new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode marshaller to marshal cloud events as JSON for
|
||||
* HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provide the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see BinaryMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> binary() {
|
||||
return
|
||||
BinaryMarshaller.<AttributesImpl, T, String, String>
|
||||
builder()
|
||||
.map(AttributesImpl::marshal)
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map)
|
||||
.map(Json.<T, String>marshaller()::marshal)
|
||||
.builder(Wire<String, String, String>::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode marshaller to marshal cloud event as JSON for
|
||||
* HTTP Transport Binding
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provider the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see StructuredMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> structured() {
|
||||
return
|
||||
StructuredMarshaller.
|
||||
<AttributesImpl, T, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, T>, String>
|
||||
marshaller().marshal(event, NO_HEADERS);
|
||||
})
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package io.cloudevents.v02.http;
|
||||
|
||||
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 <T> 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 <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type) {
|
||||
return
|
||||
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
|
||||
.map(AttributeMapper::map)
|
||||
.map(AttributesImpl::unmarshal)
|
||||
.map("application/json", Json.umarshaller(type)::unmarshal)
|
||||
.next()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.builder(CloudEventBuilder.<T>builder()::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> 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 <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData) {
|
||||
|
||||
return
|
||||
StructuredUnmarshaller.<AttributesImpl, T, String>
|
||||
builder()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<T> event =
|
||||
Json.<CloudEventImpl<T>>
|
||||
decodeValue(payload, CloudEventImpl.class, typeOfData);
|
||||
|
||||
CloudEventBuilder<T> builder =
|
||||
CloudEventBuilder.<T>builder(event);
|
||||
|
||||
extensions.get().forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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.
|
||||
*
|
||||
* <br>
|
||||
* <br>
|
||||
* 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 <A extends Attributes, T> Collection<ExtensionFormat>
|
||||
extensionsOf(CloudEvent<A, T> cloudEvent) {
|
||||
Objects.requireNonNull(cloudEvent);
|
||||
|
||||
if(cloudEvent instanceof CloudEventImpl) {
|
||||
CloudEventImpl impl = (CloudEventImpl)cloudEvent;
|
||||
return impl.getExtensionsFormats();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid instance type: "
|
||||
+ cloudEvent.getClass());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
/**
|
||||
* 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 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 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;
|
||||
|
||||
/**
|
||||
* The event attributes implementation for v0.3
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
@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,
|
||||
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;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public URI getSource() {
|
||||
return source;
|
||||
}
|
||||
public String getSpecversion() {
|
||||
return specversion;
|
||||
}
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public Optional<ZonedDateTime> getTime() {
|
||||
return Optional.ofNullable(time);
|
||||
}
|
||||
public Optional<URI> getSchemaurl() {
|
||||
return Optional.ofNullable(schemaurl);
|
||||
}
|
||||
public Optional<String> getDatacontentencoding() {
|
||||
return Optional.ofNullable(datacontentencoding);
|
||||
}
|
||||
public Optional<String> getDatacontenttype() {
|
||||
return Optional.ofNullable(datacontenttype);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Optional<String> getMediaType() {
|
||||
return getDatacontenttype();
|
||||
}
|
||||
public Optional<String> 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="
|
||||
+ 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<String, String> marshal(AttributesImpl attributes) {
|
||||
Objects.requireNonNull(attributes);
|
||||
|
||||
Map<String, String> 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<String, String> 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(schema -> URI.create(schema))
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* 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 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.CloudEvent;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.fun.EventBuilder;
|
||||
|
||||
/**
|
||||
* The event builder.
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public final class CloudEventBuilder<T> implements
|
||||
EventBuilder<T, AttributesImpl> {
|
||||
private CloudEventBuilder() {}
|
||||
|
||||
private static Validator VALIDATOR;
|
||||
|
||||
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";
|
||||
|
||||
private String id;
|
||||
private URI source;
|
||||
|
||||
private String type;
|
||||
|
||||
private ZonedDateTime time;
|
||||
private URI schemaurl;
|
||||
private String datacontentencoding;
|
||||
private String datacontenttype;
|
||||
private String subject;
|
||||
|
||||
private T data;
|
||||
|
||||
private final Set<ExtensionFormat> extensions = new HashSet<>();
|
||||
|
||||
private static Validator getValidator() {
|
||||
if(null== VALIDATOR) {
|
||||
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
return VALIDATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a brand new builder instance
|
||||
* @param <T> The 'data' type
|
||||
*/
|
||||
public static <T> CloudEventBuilder<T> builder() {
|
||||
return new CloudEventBuilder<T>();
|
||||
}
|
||||
|
||||
public static <T> CloudEventBuilder<T> builder(
|
||||
CloudEvent<AttributesImpl, T> base) {
|
||||
Objects.requireNonNull(base);
|
||||
|
||||
CloudEventBuilder<T> result = new CloudEventBuilder<>();
|
||||
|
||||
AttributesImpl attributes = base.getAttributes();
|
||||
|
||||
result
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent(time -> {
|
||||
result.withTime(time);
|
||||
});
|
||||
|
||||
attributes.getSchemaurl().ifPresent((schema) -> {
|
||||
result.withSchemaurl(schema);
|
||||
});
|
||||
|
||||
attributes.getDatacontenttype().ifPresent(dc -> {
|
||||
result.withDatacontenttype(dc);
|
||||
});
|
||||
|
||||
attributes.getDatacontentencoding().ifPresent(dce -> {
|
||||
result.withDatacontentencoding(dce);
|
||||
});
|
||||
|
||||
attributes.getSubject().ifPresent(subject -> {
|
||||
result.withSubject(subject);
|
||||
});
|
||||
|
||||
Accessor.extensionsOf(base)
|
||||
.forEach(extension -> {
|
||||
result.withExtension(extension);
|
||||
});
|
||||
|
||||
base.getData().ifPresent(data -> {
|
||||
result.withData(data);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an event from data and attributes
|
||||
* @param <T> 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 <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions) {
|
||||
CloudEventBuilder<T> builder = CloudEventBuilder.<T>builder()
|
||||
.withId(attributes.getId())
|
||||
.withSource(attributes.getSource())
|
||||
.withType(attributes.getType());
|
||||
|
||||
attributes.getTime().ifPresent((time) -> {
|
||||
builder.withTime(time);
|
||||
});
|
||||
|
||||
attributes.getSchemaurl().ifPresent((schemaurl) -> {
|
||||
builder.withSchemaurl(schemaurl);
|
||||
});
|
||||
|
||||
attributes.getDatacontentencoding().ifPresent((dce) -> {
|
||||
builder.withDatacontentencoding(dce);
|
||||
});
|
||||
|
||||
attributes.getDatacontenttype().ifPresent((dct) -> {
|
||||
builder.withDatacontenttype(dct);
|
||||
});
|
||||
|
||||
attributes.getSubject().ifPresent((subject) -> {
|
||||
builder.withSubject(subject);
|
||||
});
|
||||
|
||||
extensions.stream()
|
||||
.forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
builder.withData(data);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
|
||||
Collection<ExtensionFormat> extensions){
|
||||
return CloudEventBuilder.<T>of(data, attributes, extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return An new {@link CloudEvent} immutable instance
|
||||
* @throws IllegalStateException When there are specification constraints
|
||||
* violations
|
||||
*/
|
||||
public CloudEventImpl<T> build() {
|
||||
|
||||
AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION,
|
||||
type, time, schemaurl, datacontentencoding, datacontenttype,
|
||||
subject);
|
||||
|
||||
CloudEventImpl<T> cloudEvent =
|
||||
new CloudEventImpl<T>(attributes, data, extensions);
|
||||
|
||||
Set<ConstraintViolation<Object>> violations =
|
||||
getValidator().validate(cloudEvent);
|
||||
|
||||
violations.addAll(getValidator().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<T> withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSource(URI source) {
|
||||
this.source = source;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withTime(ZonedDateTime time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSchemaurl(URI schemaurl) {
|
||||
this.schemaurl = schemaurl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDatacontentencoding(
|
||||
String datacontentencoding) {
|
||||
this.datacontentencoding = datacontentencoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withDatacontenttype(
|
||||
String datacontenttype) {
|
||||
this.datacontenttype = datacontenttype;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withSubject(
|
||||
String subject) {
|
||||
this.subject = subject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withData(T data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CloudEventBuilder<T> withExtension(ExtensionFormat extension) {
|
||||
this.extensions.add(extension);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* 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<T> implements CloudEvent<AttributesImpl, T> {
|
||||
|
||||
@JsonIgnore
|
||||
@NotNull
|
||||
private final AttributesImpl attributes;
|
||||
|
||||
private final T data;
|
||||
|
||||
@NotNull
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private final Set<ExtensionFormat> extensionsFormats;
|
||||
|
||||
CloudEventImpl(AttributesImpl attributes, T data,
|
||||
Set<ExtensionFormat> 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<ExtensionFormat> getExtensionsFormats() {
|
||||
return extensionsFormats;
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
@Override
|
||||
public AttributesImpl getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> getData() {
|
||||
return Optional.ofNullable(data);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
@Override
|
||||
public Map<String, Object> 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 <T> CloudEventImpl<T> 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("datacontentencoding") String datacontentencoding,
|
||||
@JsonProperty("datacontenttype") String datacontenttype,
|
||||
@JsonProperty("subject") String subject,
|
||||
@JsonProperty("data") T data) {
|
||||
|
||||
return CloudEventBuilder.<T>builder()
|
||||
.withId(id)
|
||||
.withSource(source)
|
||||
.withType(type)
|
||||
.withTime(time)
|
||||
.withSchemaurl(schemaurl)
|
||||
.withDatacontentencoding(datacontentencoding)
|
||||
.withDatacontenttype(datacontenttype)
|
||||
.withData(data)
|
||||
.withSubject(subject)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* 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.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,
|
||||
datacontenttype,
|
||||
datacontentencoding,
|
||||
subject;
|
||||
|
||||
public static final List<String> VALUES =
|
||||
Arrays.asList(ContextAttributes.values())
|
||||
.stream()
|
||||
.map(Enum::name)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* 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<String, String> map(final Map<String, Object> headers) {
|
||||
Objects.requireNonNull(headers);
|
||||
|
||||
final AtomicReference<Optional<Entry<String, Object>>> ct =
|
||||
new AtomicReference<>();
|
||||
|
||||
ct.set(Optional.empty());
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* 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.2
|
||||
*/
|
||||
public class ExtensionMapper {
|
||||
private ExtensionMapper() {}
|
||||
|
||||
private static final List<String> 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<String, String> map(Map<String, Object> 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 -> {
|
||||
return !RESERVED_HEADERS.contains(header.getKey());
|
||||
})
|
||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* 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<String, String> map(Map<String, String> attributes,
|
||||
Map<String, String> extensions) {
|
||||
Objects.requireNonNull(attributes);
|
||||
Objects.requireNonNull(extensions);
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* 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<String, String> NO_HEADERS =
|
||||
new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Builds a Binary Content Mode marshaller to marshal cloud events as JSON for
|
||||
* HTTP Transport Binding
|
||||
*
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provide the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see BinaryMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> binary() {
|
||||
return
|
||||
BinaryMarshaller.<AttributesImpl, T, String, String>
|
||||
builder()
|
||||
.map(AttributesImpl::marshal)
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map)
|
||||
.map(Json.<T, String>marshaller()::marshal)
|
||||
.builder(Wire<String, String, String>::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode marshaller to marshal cloud event as JSON for
|
||||
* HTTP Transport Binding
|
||||
* @param <T> The 'data' type
|
||||
* @return A step to provider the {@link CloudEventImpl} and marshal as JSON
|
||||
* @see StructuredMarshaller
|
||||
*/
|
||||
public static <T> EventStep<AttributesImpl, T, String, String> structured() {
|
||||
return
|
||||
StructuredMarshaller.
|
||||
<AttributesImpl, T, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((event) -> {
|
||||
return Json.<CloudEvent<AttributesImpl, T>, String>
|
||||
marshaller().marshal(event, NO_HEADERS);
|
||||
})
|
||||
.map(Accessor::extensionsOf)
|
||||
.map(ExtensionFormat::marshal)
|
||||
.map(HeaderMapper::map);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* 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 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 <T> 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 <T> HeadersStep<AttributesImpl, T, String>
|
||||
binary(Class<T> type) {
|
||||
return
|
||||
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
|
||||
.map(AttributeMapper::map)
|
||||
.map(AttributesImpl::unmarshal)
|
||||
.map("application/json", Json.umarshaller(type)::unmarshal)
|
||||
.next()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.builder(CloudEventBuilder.<T>builder()::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Structured Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
||||
* for HTTP Transport Binding
|
||||
*
|
||||
* @param <T> 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 <T> HeadersStep<AttributesImpl, T, String>
|
||||
structured(Class<T> typeOfData) {
|
||||
|
||||
return
|
||||
StructuredUnmarshaller.<AttributesImpl, T, String>
|
||||
builder()
|
||||
.map(ExtensionMapper::map)
|
||||
.map(DistributedTracingExtension::unmarshall)
|
||||
.next()
|
||||
.map((payload, extensions) -> {
|
||||
CloudEventImpl<T> event =
|
||||
Json.<CloudEventImpl<T>>
|
||||
decodeValue(payload, CloudEventImpl.class, typeOfData);
|
||||
|
||||
CloudEventBuilder<T> builder =
|
||||
CloudEventBuilder.<T>builder(event);
|
||||
|
||||
extensions.get().forEach(extension -> {
|
||||
builder.withExtension(extension);
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,198 +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;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CloudEventBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testBuilderWithData() {
|
||||
|
||||
// given
|
||||
final Map<String, String> keyValueStore = new HashMap<>();
|
||||
keyValueStore.put("key1", "value1");
|
||||
keyValueStore.put("key2", "value2");
|
||||
final String id = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("/trigger");
|
||||
final String type = "My.Cloud.Event.Type";
|
||||
final ZonedDateTime eventTime = ZonedDateTime.now();
|
||||
final String contentType = "application/json";
|
||||
final URI schemaUri = URI.create("http://cloudevents.io/schema");
|
||||
|
||||
// when
|
||||
final CloudEvent<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.data(keyValueStore)
|
||||
.contentType(contentType)
|
||||
.type(type)
|
||||
.schemaURL(schemaUri)
|
||||
.time(eventTime)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.build();
|
||||
|
||||
// than
|
||||
simpleKeyValueEvent.getData().ifPresent(data -> {
|
||||
assertThat(data).isNotNull();
|
||||
assertThat(data).containsKeys("key1", "key2");
|
||||
assertThat(data).containsValues("value1", "value2");
|
||||
});
|
||||
|
||||
assertThat(simpleKeyValueEvent.getContentType().get()).isEqualTo(contentType);
|
||||
assertThat(simpleKeyValueEvent.getTime().get()).isEqualTo(eventTime);
|
||||
assertThat(simpleKeyValueEvent.getId()).isEqualTo(id);
|
||||
assertThat(simpleKeyValueEvent.getSchemaURL().get()).isEqualTo(schemaUri);
|
||||
assertThat(simpleKeyValueEvent.getType()).isEqualTo(type);
|
||||
assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src);
|
||||
assertThat(simpleKeyValueEvent.getSpecVersion()).isEqualTo(SpecVersion.DEFAULT.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderWithoutData() {
|
||||
|
||||
// given
|
||||
final String id = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("/trigger");
|
||||
final String type = "My.Cloud.Event.Type";
|
||||
|
||||
// when
|
||||
final CloudEvent<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.build();
|
||||
// than
|
||||
assertThat(simpleKeyValueEvent.getData().isPresent()).isFalse();
|
||||
assertThat(simpleKeyValueEvent.getTime().isPresent()).isFalse();
|
||||
assertThat(simpleKeyValueEvent.getId()).isEqualTo(id);
|
||||
assertThat(simpleKeyValueEvent.getType()).isEqualTo(type);
|
||||
assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src);
|
||||
assertThat(simpleKeyValueEvent.getSpecVersion()).isEqualTo(SpecVersion.DEFAULT.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderWithoutDataAndUrn() {
|
||||
|
||||
// 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<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.build();
|
||||
// than
|
||||
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<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.specVersion("0.1")
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.build();
|
||||
// than
|
||||
assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src);
|
||||
assertThat(simpleKeyValueEvent.getSpecVersion()).isEqualTo(SpecVersion.V_01.toString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderWithoutDataAndURISchema() {
|
||||
|
||||
// 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";
|
||||
final URI schema = URI.create("urn:oasis:names:specification:docbook:dtd:xml:4.1.2");
|
||||
|
||||
// when
|
||||
final CloudEvent<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.schemaURL(schema)
|
||||
.build();
|
||||
// than
|
||||
assertThat(simpleKeyValueEvent.getSchemaURL().get()).isEqualTo(schema);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderWithoutDataAndMailto() {
|
||||
|
||||
// given
|
||||
final String id = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("mailto:cncf-wg-serverless@lists.cncf.io");
|
||||
final String type = "My.Cloud.Event.Type";
|
||||
|
||||
// when
|
||||
final CloudEvent<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.build();
|
||||
// than
|
||||
assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderWithoutDataAndDistributedTracingExtension() {
|
||||
|
||||
// given
|
||||
final String id = UUID.randomUUID().toString();
|
||||
final URI src = URI.create("mailto:cncf-wg-serverless@lists.cncf.io");
|
||||
final String type = "My.Cloud.Event.Type";
|
||||
final DistributedTracingExtension dte = new DistributedTracingExtension();
|
||||
dte.setTraceparent("00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01");
|
||||
dte.setTracestate("congo=BleGNlZWRzIHRohbCBwbGVhc3VyZS4");
|
||||
|
||||
// when
|
||||
final CloudEvent<Map<String, String>> simpleKeyValueEvent = new CloudEventBuilder()
|
||||
.type(type)
|
||||
.id(id)
|
||||
.source(src)
|
||||
.extension(dte)
|
||||
.build();
|
||||
// than
|
||||
assertThat(simpleKeyValueEvent.getSource()).isEqualTo(src);
|
||||
assertThat(simpleKeyValueEvent.getExtensions().get()).contains(dte);
|
||||
|
||||
Extension receivedDte = simpleKeyValueEvent.getExtensions().get().get(0);
|
||||
assertThat(receivedDte).extracting("traceparent", "tracestate")
|
||||
.contains("00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01", "congo=BleGNlZWRzIHRohbCBwbGVhc3VyZS4");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +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;
|
||||
|
||||
import io.cloudevents.json.Json;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class CloudEventJacksonTest {
|
||||
|
||||
@Test
|
||||
public void testParseAzure01JSON() {
|
||||
CloudEvent<Map<String, ?>> ce = Json.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("01_azure.json"));
|
||||
assertThat(ce.getSpecVersion()).isEqualTo(SpecVersion.V_01.toString());
|
||||
assertAzureCloudEvent(ce);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAzure02JSON() {
|
||||
CloudEvent<Map<String, ?>> ce = Json.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("02_azure.json"));
|
||||
assertThat(ce.getSpecVersion()).isEqualTo(SpecVersion.V_02.toString());
|
||||
assertAzureCloudEvent(ce);
|
||||
}
|
||||
|
||||
private void assertAzureCloudEvent(CloudEvent<Map<String, ?>> ce) {
|
||||
assertThat(ce.getType()).isEqualTo("Microsoft.Storage.BlobCreated");
|
||||
|
||||
ce.getData().ifPresent(data -> {
|
||||
assertThat(Map.class).isAssignableFrom(data.getClass());
|
||||
assertThat(data.get("clientRequestId")).isEqualTo("a23b4aba-2755-4107-8020-8ba6c54b203d");
|
||||
assertThat(Map.class).isAssignableFrom(data.get("storageDiagnostics").getClass());
|
||||
Map<String, String> storageDiagnostics = (Map<String, String>) data.get("storageDiagnostics");
|
||||
assertThat(storageDiagnostics).containsOnlyKeys("batchId");
|
||||
assertThat(storageDiagnostics.get("batchId")).isEqualTo("ba4fb664-f289-4742-8067-6c859411b066");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAmazon01JSON() {
|
||||
CloudEvent ce = Json.fromInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream("01_aws.json"));
|
||||
assertAmazonCloudEvent(ce);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseAmazon02JSON() {
|
||||
CloudEvent ce = Json.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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* 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 static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fabiojose
|
||||
*
|
||||
*/
|
||||
public class DistributedTracingExtensionTest {
|
||||
|
||||
@Test
|
||||
public void should_transport_format_ok() {
|
||||
// setup
|
||||
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"));
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* 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.<Attributes, Much, String, String>builder()
|
||||
.mime(null, "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_envelope_mime_value() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
// act
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_be_ok_on_the_first_step() {
|
||||
|
||||
// act
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_marshaller_step() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
// act
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_event() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.skip()
|
||||
.withEvent(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_be_ok_on_the_third_step() {
|
||||
// act
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.skip()
|
||||
.withEvent(() -> null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_extension_accessor() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_the_extension_acessor() {
|
||||
// act
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((event) -> {
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_extension_marshaller() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((event) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_extension_marshaller() {
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((event) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((extensions) -> {
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_header_mapper() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((event) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((extensions) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_header_mapper() {
|
||||
StructuredMarshaller.<Attributes, Much, String, String>builder()
|
||||
.mime("Content-Type", "application/cloudevents+json")
|
||||
.map((ce) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((event) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((extensions) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((attributes, extensions) -> null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/**
|
||||
* 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.<Attributes, Much, String>builder()
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_extension_mapper() {
|
||||
StructuredUnmarshaller.<Attributes, Much, String>builder()
|
||||
.map((headers) -> {
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_extension_unmarshaller() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredUnmarshaller.<Attributes, Much, String>builder()
|
||||
.map((headers) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_extension_unmarshaller() {
|
||||
StructuredUnmarshaller.<Attributes, Much, String>builder()
|
||||
.map((headers) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((extensions) -> {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_on_null_envelope_unmarshaller() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
StructuredUnmarshaller.<Attributes, Much, String>builder()
|
||||
.map((headers) -> {
|
||||
|
||||
return null;
|
||||
})
|
||||
.map((extensions) -> {
|
||||
return null;
|
||||
})
|
||||
.next()
|
||||
.map(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_envelope_unmarshaller() {
|
||||
StructuredUnmarshaller.<Attributes, Much, String>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.<Attributes, Much, String>builder()
|
||||
.map((headers) -> null)
|
||||
.map((extensions) -> null)
|
||||
.next()
|
||||
.map((payload, extensions) -> null)
|
||||
.withHeaders(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_on_headers() {
|
||||
StructuredUnmarshaller.<Attributes, Much, String>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.<Attributes, Much, String>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.<Attributes, Much, String>builder()
|
||||
.map((headers) -> null)
|
||||
.map((extensions) -> null)
|
||||
.next()
|
||||
.map((payload, extensions) -> null)
|
||||
.withHeaders(() -> null)
|
||||
.withPayload(() -> null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* 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<String, String, Object>("payload", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throws_when_try_to_change_headers() {
|
||||
// setup
|
||||
expectedEx.expect(UnsupportedOperationException.class);
|
||||
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("contenttype", "application/json");
|
||||
|
||||
// act
|
||||
Wire<String, String, Object> wire = new Wire<>("payload", headers);
|
||||
|
||||
wire.getHeaders().put("my-header", "my-header-val");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_when_null_payload() {
|
||||
Wire<String, String, Object> expected =
|
||||
new Wire<>(null, new HashMap<>());
|
||||
|
||||
assertFalse(expected.getPayload().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_when_payload_not_null() {
|
||||
Wire<String, String, Object> actual =
|
||||
new Wire<>("payload", new HashMap<>());
|
||||
|
||||
assertTrue(actual.getPayload().isPresent());
|
||||
assertEquals("payload", actual.getPayload().get());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,38 +15,92 @@
|
|||
*/
|
||||
package io.cloudevents.http;
|
||||
|
||||
import io.cloudevents.SpecVersion;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
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() {
|
||||
// setup
|
||||
Map<String, Object> 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");
|
||||
|
||||
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");
|
||||
// act
|
||||
Map<String, String> attributes = io.cloudevents.v02.http.AttributeMapper.map(myHeaders);
|
||||
|
||||
// non-changed between 01 / 02
|
||||
assertThat(v02.sourceKey()).isEqualTo("ce-source");
|
||||
// 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<String, String> 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<String, String> headers = io.cloudevents.v02.http.HeaderMapper
|
||||
.map(attributes, new HashMap<String, String>());
|
||||
|
||||
// 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<String, Object> 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<String, String> 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"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
package io.cloudevents.json;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.CloudEventBuilder;
|
||||
import io.cloudevents.json.types.GlusterVolumeClaim;
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -35,22 +36,23 @@ public class CustomEventTypesTest {
|
|||
|
||||
// given
|
||||
final Map<String, Object> storagePayload = (MAPPER.readValue(Thread.currentThread().getContextClassLoader().getResourceAsStream("pvc.json"), Map.class));
|
||||
final CloudEvent<Map<String, Object>> storageCloudEventWrapper = new CloudEventBuilder<Map<String, Object>>()
|
||||
.type("ProvisioningSucceeded")
|
||||
.source(URI.create("/scheduler"))
|
||||
.id(UUID.randomUUID().toString())
|
||||
.data(storagePayload)
|
||||
final CloudEventImpl<Map<String, Object>> storageCloudEventWrapper = CloudEventBuilder.<Map<String, Object>>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 CloudEvent<GlusterVolumeClaim> event = Json.decodeValue(httpSerializedPayload, new TypeReference<CloudEvent<GlusterVolumeClaim>>() {});
|
||||
final CloudEventImpl<GlusterVolumeClaim> event = Json.decodeValue(httpSerializedPayload, new TypeReference<CloudEventImpl<GlusterVolumeClaim>>() {});
|
||||
|
||||
// then
|
||||
assertThat(event.getData().get()).isNotNull();
|
||||
assertThat(event.getData().get().getSpec().getCapacity().get("storage")).isEqualTo("2Gi");
|
||||
assertThat(event.getData().get().getSpec().getAccessModes()).containsExactly("ReadWriteMany");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* 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<Map<String, Object>>() {});
|
||||
|
||||
// assert
|
||||
assertNull(actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_result_null_on_decode_typereference_null_string() {
|
||||
|
||||
// act
|
||||
Object actual = Json.decodeValue(null, new TypeReference<Map<String, Object>>() {});
|
||||
|
||||
// assert
|
||||
assertNull(actual);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* 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<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withSchemaurl(URI.create("/schema"))
|
||||
.withContenttype("text/plain")
|
||||
.withData("my-data")
|
||||
.build();
|
||||
|
||||
// act
|
||||
Collection<ExtensionFormat> 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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<ExtensionFormat> 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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<ExtensionFormat> 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,22 @@
|
|||
/**
|
||||
* 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 java.net.URI;
|
||||
|
|
@ -10,6 +26,12 @@ 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
|
||||
|
|
@ -27,7 +49,7 @@ public class CloudEventBuilderTest {
|
|||
expectedEx.expectMessage("invalid payload: 'id' must not be blank");
|
||||
|
||||
// act
|
||||
new CloudEventBuilder<Object>()
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withSource(URI.create("/test"))
|
||||
.withType("type")
|
||||
.build();
|
||||
|
|
@ -40,7 +62,7 @@ public class CloudEventBuilderTest {
|
|||
expectedEx.expectMessage("invalid payload: 'id' must not be blank");
|
||||
|
||||
// act
|
||||
new CloudEventBuilder<Object>()
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("")
|
||||
.withSource(URI.create("/test"))
|
||||
.withType("type")
|
||||
|
|
@ -54,7 +76,7 @@ public class CloudEventBuilderTest {
|
|||
expectedEx.expectMessage("invalid payload: 'type' must not be blank");
|
||||
|
||||
// act
|
||||
new CloudEventBuilder<Object>()
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/test"))
|
||||
.build();
|
||||
|
|
@ -67,7 +89,7 @@ public class CloudEventBuilderTest {
|
|||
expectedEx.expectMessage("invalid payload: 'type' must not be blank");
|
||||
|
||||
// act
|
||||
new CloudEventBuilder<Object>()
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/test"))
|
||||
.withType("")
|
||||
|
|
@ -81,7 +103,7 @@ public class CloudEventBuilderTest {
|
|||
expectedEx.expectMessage("invalid payload: 'source' must not be null");
|
||||
|
||||
// act
|
||||
new CloudEventBuilder<Object>()
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withType("type")
|
||||
.build();
|
||||
|
|
@ -90,57 +112,57 @@ public class CloudEventBuilderTest {
|
|||
@Test
|
||||
public void should_have_id() {
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.build();
|
||||
|
||||
// assert
|
||||
assertEquals("id", ce.getId());
|
||||
assertEquals("id", ce.getAttributes().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_source() {
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.build();
|
||||
|
||||
// assert
|
||||
assertEquals(URI.create("/source"), ce.getSource());
|
||||
assertEquals(URI.create("/source"), ce.getAttributes().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_type() {
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.build();
|
||||
|
||||
// assert
|
||||
assertEquals("type", ce.getType());
|
||||
assertEquals("type", ce.getAttributes().getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_specversion() {
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.build();
|
||||
|
||||
// assert
|
||||
assertEquals("0.2", ce.getSpecversion());
|
||||
assertEquals("0.2", ce.getAttributes().getSpecversion());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -149,8 +171,8 @@ public class CloudEventBuilderTest {
|
|||
ZonedDateTime expected = ZonedDateTime.now();
|
||||
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
|
|
@ -158,8 +180,8 @@ public class CloudEventBuilderTest {
|
|||
.build();
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getTime().isPresent());
|
||||
assertEquals(expected, ce.getTime().get());
|
||||
assertTrue(ce.getAttributes().getTime().isPresent());
|
||||
assertEquals(expected, ce.getAttributes().getTime().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -168,8 +190,8 @@ public class CloudEventBuilderTest {
|
|||
URI expected = URI.create("/schema");
|
||||
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
|
|
@ -177,8 +199,8 @@ public class CloudEventBuilderTest {
|
|||
.build();
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getSchemaurl().isPresent());
|
||||
assertEquals(expected, ce.getSchemaurl().get());
|
||||
assertTrue(ce.getAttributes().getSchemaurl().isPresent());
|
||||
assertEquals(expected, ce.getAttributes().getSchemaurl().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -187,8 +209,8 @@ public class CloudEventBuilderTest {
|
|||
String expected = "application/json";
|
||||
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
|
|
@ -196,8 +218,8 @@ public class CloudEventBuilderTest {
|
|||
.build();
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getContenttype().isPresent());
|
||||
assertEquals(expected, ce.getContenttype().get());
|
||||
assertTrue(ce.getAttributes().getContenttype().isPresent());
|
||||
assertEquals(expected, ce.getAttributes().getContenttype().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -206,8 +228,8 @@ public class CloudEventBuilderTest {
|
|||
String expected = "my data";
|
||||
|
||||
// act
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
|
|
@ -218,4 +240,82 @@ public class CloudEventBuilderTest {
|
|||
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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<Much> base =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.withData(data)
|
||||
.build();
|
||||
|
||||
// act
|
||||
CloudEvent<AttributesImpl, String> actual =
|
||||
CloudEventBuilder.<Much>builder().build(base, "0x010", expected);
|
||||
|
||||
// assert
|
||||
assertTrue(actual.getData().isPresent());
|
||||
assertEquals(expected, actual.getData().get());
|
||||
assertEquals("0x010", actual.getAttributes().getId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/**
|
||||
* 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;
|
||||
|
|
@ -13,6 +28,7 @@ import org.junit.Test;
|
|||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
||||
import io.cloudevents.extensions.ExtensionFormat;
|
||||
import io.cloudevents.json.Json;
|
||||
|
||||
/**
|
||||
|
|
@ -32,8 +48,8 @@ public class CloudEventJacksonTest {
|
|||
@Test
|
||||
public void should_encode_right_with_minimal_attrs() {
|
||||
// setup
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
|
|
@ -57,8 +73,8 @@ public class CloudEventJacksonTest {
|
|||
@Test
|
||||
public void should_have_optional_attrs() {
|
||||
// setup
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
|
|
@ -84,10 +100,10 @@ public class CloudEventJacksonTest {
|
|||
dt.setTraceparent("0");
|
||||
dt.setTracestate("congo=4");
|
||||
|
||||
final ExtensionFormat tracing = new DistributedTracingExtension.InMemory(dt);
|
||||
final ExtensionFormat tracing = new DistributedTracingExtension.Format(dt);
|
||||
|
||||
CloudEvent<Object> ce =
|
||||
new CloudEventBuilder<>()
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
|
|
@ -104,60 +120,80 @@ public class CloudEventJacksonTest {
|
|||
assertTrue(actual.contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_serialize_attributes_element() {
|
||||
// setup
|
||||
CloudEventImpl<Object> ce =
|
||||
CloudEventBuilder.<Object>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
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertEquals("aws.s3.object.created", ce.getType());
|
||||
assertEquals("aws.s3.object.created", ce.getAttributes().getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_id() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertEquals("C234-1234-1234", ce.getId());
|
||||
assertEquals("C234-1234-1234", ce.getAttributes().getId());
|
||||
}
|
||||
|
||||
//should have time
|
||||
@Test
|
||||
public void should_have_time() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getTime().isPresent());
|
||||
assertTrue(ce.getAttributes().getTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_source() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertEquals(URI.create("https://serverless.com"), ce.getSource());
|
||||
assertEquals(URI.create("https://serverless.com"), ce.getAttributes().getSource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_contenttype() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_aws.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_aws.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getContenttype().isPresent());
|
||||
assertEquals("application/json", ce.getContenttype().get());
|
||||
assertTrue(ce.getAttributes().getContenttype().isPresent());
|
||||
assertEquals("application/json", ce.getAttributes().getContenttype().get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_specversion() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertEquals("0.2", ce.getSpecversion());
|
||||
assertEquals("0.2", ce.getAttributes().getSpecversion());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -167,17 +203,17 @@ public class CloudEventJacksonTest {
|
|||
expectedEx.expectMessage("invalid payload: 'id' must not be blank");
|
||||
|
||||
// act
|
||||
Json.fromInputStream(resourceOf("02_absent.json"), CloudEvent.class);
|
||||
Json.fromInputStream(resourceOf("02_absent.json"), CloudEventImpl.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_tracing_extension() {
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertNotNull(ce.getExtensions()
|
||||
.get(DistributedTracingExtension.InMemory.IN_MEMORY_KEY));
|
||||
.get(DistributedTracingExtension.Format.IN_MEMORY_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -187,7 +223,7 @@ public class CloudEventJacksonTest {
|
|||
String expected = "extension-value";
|
||||
|
||||
// act
|
||||
CloudEvent<?> ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEvent.class);
|
||||
CloudEventImpl<?> ce = Json.fromInputStream(resourceOf("02_extension.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertEquals(expected, ce.getExtensions()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* 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<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce_specversion", null);
|
||||
|
||||
// act
|
||||
Map<String, String> actual = AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertFalse(actual.containsKey("specversion"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_when_no_content_type() {
|
||||
// setup
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-specversion", "0.2");
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertFalse(attributes.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_map_cespecversion_to_specversion() {
|
||||
// setup
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-specversion", "0.2");
|
||||
headers.put("Content-Type", "application/json");
|
||||
|
||||
String expected = "specversion";
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertNotNull(attributes.get(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_null_value() {
|
||||
// setup
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-type", null);
|
||||
|
||||
String expected = "type";
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertFalse(attributes.containsKey(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_all_without_prefix_ce() {
|
||||
// setup
|
||||
Map<String, Object> 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<String, String> actual = AttributeMapper.map(myHeaders);
|
||||
|
||||
actual.keySet()
|
||||
.forEach((attribute) -> {
|
||||
assertFalse(attribute.startsWith("ce-"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* 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<String, Object> 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<String, String> actual = ExtensionMapper.map(myHeaders);
|
||||
|
||||
|
||||
// assert
|
||||
assertFalse(actual.containsKey(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_just_potential_extensions() {
|
||||
// setup
|
||||
Map<String, Object> 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<String, String> 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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* 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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("application/json")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>
|
||||
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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("application/json")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>
|
||||
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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* 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<String, Object> 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<AttributesImpl, Much> 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<String, Object> 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<AttributesImpl, Much> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* 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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("application/json")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* 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<String, Object> 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<Much> expected =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("application/json")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
CloudEvent<AttributesImpl, Much> 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<String, Object> 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<String> expected =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withContenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
CloudEvent<AttributesImpl, String> 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<String, Object> 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<AttributesImpl, String> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/**
|
||||
* 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<String, String> extensions = new HashMap<>();
|
||||
|
||||
// act
|
||||
HeaderMapper.map(null, extensions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void error_when_extensions_map_isnull() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
// act
|
||||
HeaderMapper.map(attributes, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_null_attribute_value() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", null);
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("ce-type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_null_extension_value() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", "mytype");
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
extensions.put("null-ext", null);
|
||||
extensions.put("comexampleextension1", "value");
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("null-ext"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_absent_contenttype() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", "mytype");
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
extensions.put("null-ext", "null-value");
|
||||
extensions.put("comexampleextension1", "value");
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("Content-Type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_map_extension_without_prefix() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", "mytype");
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
extensions.put("null-ext", "null-value");
|
||||
extensions.put("comexampleextension1", "value");
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertTrue(actual.containsKey("comexampleextension1"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* 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<Object> ce =
|
||||
CloudEventBuilder.<Object>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withSchemaurl(URI.create("/schema"))
|
||||
.withDatacontenttype("text/plain")
|
||||
.withData("my-data")
|
||||
.build();
|
||||
|
||||
// act
|
||||
Collection<ExtensionFormat> 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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<ExtensionFormat> 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<Object> ce =
|
||||
CloudEventBuilder.<Object>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<ExtensionFormat> 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* 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.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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.<Object>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.<Object>builder()
|
||||
.withId("id")
|
||||
.withType("type")
|
||||
.withSource(URI.create("/source"))
|
||||
.withSubject("subject")
|
||||
.withDatacontentencoding("binary")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_subject() {
|
||||
// act
|
||||
CloudEvent<AttributesImpl, Object> ce =
|
||||
CloudEventBuilder.<Object>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<Object> 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<Object> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
/**
|
||||
* 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> ce =
|
||||
Json.fromInputStream(resourceOf("03_new.json"), CloudEventImpl.class);
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getAttributes().getTime().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_have_source() {
|
||||
// act
|
||||
CloudEvent<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Object> 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<AttributesImpl, Much> ce =
|
||||
Json.decodeValue(json, new TypeReference<CloudEventImpl<Much>>() {});
|
||||
|
||||
// assert
|
||||
assertTrue(ce.getData().isPresent());
|
||||
assertEquals(expected.getWow(), ce.getData().get().getWow());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* 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<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-type", null);
|
||||
|
||||
String expected = "type";
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertFalse(attributes.containsKey(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_ok_when_no_content_type() {
|
||||
// setup
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-specversion", "0.3");
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertFalse(attributes.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_map_cespecversion_to_specversion() {
|
||||
// setup
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("ce-specversion", "0.3");
|
||||
headers.put("Content-Type", "application/json");
|
||||
|
||||
String expected = "specversion";
|
||||
|
||||
// act
|
||||
Map<String, String> attributes =
|
||||
AttributeMapper.map(headers);
|
||||
|
||||
// assert
|
||||
assertNotNull(attributes.get(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_all_without_prefix_ce() {
|
||||
// setup
|
||||
Map<String, Object> 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<String, String> actual = AttributeMapper.map(myHeaders);
|
||||
|
||||
actual.keySet()
|
||||
.forEach((attribute) -> {
|
||||
assertFalse(attribute.startsWith("ce-"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* 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<String, Object> 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<String, String> actual = ExtensionMapper.map(myHeaders);
|
||||
|
||||
|
||||
// assert
|
||||
assertFalse(actual.containsKey(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_just_potential_extensions() {
|
||||
// setup
|
||||
Map<String, Object> 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<String, String> 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"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* 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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("application/json")
|
||||
.withSubject("subject")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>
|
||||
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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("application/json")
|
||||
.withSubject("subject")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>
|
||||
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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* 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<String, Object> 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<AttributesImpl, Much> 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<String, Object> 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<AttributesImpl, Much> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* 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<Much> ce =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("application/json")
|
||||
.withSubject("subject")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<Much>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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<String> ce =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("id")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("type")
|
||||
.withExtension(tracing)
|
||||
.build();
|
||||
|
||||
// act
|
||||
Wire<String, String, String> actual =
|
||||
Marshallers.<String>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));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* 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<String, Object> 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<Much> expected =
|
||||
CloudEventBuilder.<Much>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("application/json")
|
||||
.withSubject("subject")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
CloudEvent<AttributesImpl, Much> 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<String, Object> 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<String> expected =
|
||||
CloudEventBuilder.<String>builder()
|
||||
.withId("x10")
|
||||
.withSource(URI.create("/source"))
|
||||
.withType("event-type")
|
||||
.withDatacontenttype("text/plain")
|
||||
.withData(ceData)
|
||||
.build();
|
||||
|
||||
// act
|
||||
CloudEvent<AttributesImpl, String> 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<String, Object> 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<AttributesImpl, String> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* 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<String, String> extensions = new HashMap<>();
|
||||
|
||||
// act
|
||||
HeaderMapper.map(null, extensions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void error_when_extensions_map_isnull() {
|
||||
// setup
|
||||
expectedEx.expect(NullPointerException.class);
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
// act
|
||||
HeaderMapper.map(attributes, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_null_attribute_value() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", null);
|
||||
attributes.put("specversion", "0.3");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("ce-type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_null_extension_value() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", "mytype");
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
extensions.put("null-ext", null);
|
||||
extensions.put("comexampleextension1", "value");
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("ce-null-ext"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_map_absent_datacontenttype() {
|
||||
// setup
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put("type", "mytype");
|
||||
attributes.put("specversion", "0.2");
|
||||
|
||||
Map<String, String> extensions = new HashMap<>();
|
||||
extensions.put("null-ext", "null-value");
|
||||
extensions.put("comexampleextension1", "value");
|
||||
|
||||
// act
|
||||
Map<String, String> actual = HeaderMapper.map(attributes, extensions);
|
||||
|
||||
//assert
|
||||
assertFalse(actual.containsKey("Content-Type"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "aws.s3.object.created",
|
||||
"time": "2018-04-26T14:48:09.769Z",
|
||||
"source": "https://serverless.com",
|
||||
"datacontenttype": "application/json",
|
||||
"specversion": "0.3"
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "aws.s3.object.created",
|
||||
"id": "C234-1234-1234",
|
||||
"time": "2018-04-26T14:48:09.769Z",
|
||||
"source": "https://serverless.com",
|
||||
"datacontenttype": "application/json",
|
||||
"datacontentencoding": "base64",
|
||||
"specversion": "0.3",
|
||||
"data":"eyJzM1NjaGVtYVZlcnNpb24iOiIxLjAiLCJjb25maWd1cmF0aW9uSWQiOiJjZDI2N2EzOC0zMGRmLTQwMGUtOWUzZC1kMGYxY2E2ZTI0MTAiLCJidWNrZXQiOnsibmFtZSI6ImNsb3VkZXZlbnRzIiwib3duZXJJZGVudGl0eSI6e30sImFybiI6ImFybjphd3M6czM6OjpjbG91ZGV2ZW50cyJ9LCJvYmplY3QiOnsia2V5IjoiZGFuX2tvaG4uanBnIiwic2l6ZSI6NDQ0Njg0LCJlVGFnIjoiMzhiMDFmZjE2MTM4ZDdjYTBhMGViM2Y3YTg4ZmY4MTUiLCJzZXF1ZW5jZXIiOiIwMDVBRTFFNkE5QTNENjE0OTAifX0K",
|
||||
"my-extension" : "extension-value"
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"type": "aws.s3.object.created",
|
||||
"id": "C234-1234-1234",
|
||||
"time": "2018-04-26T14:48:09.769Z",
|
||||
"source": "https://serverless.com",
|
||||
"datacontenttype": "application/json",
|
||||
"specversion": "0.3",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"my-extension" : "extension-value",
|
||||
"distributedTracing": {
|
||||
"traceparent": "0",
|
||||
"tracestate": "congo=4"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"type": "aws.s3.object.created",
|
||||
"id": "C234-1234-1234",
|
||||
"time": "2018-04-26T14:48:09.769Z",
|
||||
"source": "https://serverless.com",
|
||||
"datacontenttype": "application/json",
|
||||
"specversion": "0.3",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"my-extension" : "extension-value"
|
||||
}
|
||||
|
|
@ -8,15 +8,15 @@ For Maven based projects, use the following to configure the CloudEvents CDI lib
|
|||
<dependency>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cdi</artifactId>
|
||||
<version>0.2.0</version>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
In _Enterprise Java_ applications, implemented with [Jakarta EE](https://jakarta.ee/) or the [Eclipse MicroProfile](https://microprofile.io/), it's trivial to combine this CloudEvents API with CDI. Application developers can now fire a CloudEvent for further processing inside of the application:
|
||||
|
||||
```java
|
||||
import io.cloudevents.CloudEvent;
|
||||
import io.cloudevents.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventBuilder;
|
||||
import io.cloudevents.v02.CloudEventImpl;
|
||||
import io.cloudevents.cdi.EventTypeQualifier;
|
||||
|
||||
import javax.enterprise.event.Event;
|
||||
|
|
@ -28,15 +28,16 @@ import java.util.UUID;
|
|||
public class Router {
|
||||
|
||||
@Inject
|
||||
private Event<CloudEvent<MyCustomEvent>> cloudEvent;
|
||||
private Event<CloudEventImpl<MyCustomEvent>> cloudEvent;
|
||||
|
||||
public void routeMe() throws Exception {
|
||||
|
||||
final CloudEvent<MyCustomEvent> event = new CloudEventBuilder<MyCustomEvent>()
|
||||
.type("Cloud.Storage.Item.Created")
|
||||
.source(new URI("/trigger"))
|
||||
.id(UUID.randomUUID().toString())
|
||||
.data(new MyCustomEvent(...))
|
||||
final CloudEventImpl<MyCustomEvent> event =
|
||||
CloudEventBuilder.<MyCustomEvent>builder()
|
||||
.withType("Cloud.Storage.Item.Created")
|
||||
.withSource(new URI("/trigger"))
|
||||
.withId(UUID.randomUUID().toString())
|
||||
.withData(new MyCustomEvent(...))
|
||||
.build();
|
||||
|
||||
cloudEvent.select(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<parent>
|
||||
<groupId>io.cloudevents</groupId>
|
||||
<artifactId>cloudevents-parent</artifactId>
|
||||
<version>0.2.2-SNAPSHOT</version>
|
||||
<version>0.3.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
@ -41,11 +41,11 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Java EE 7 dependency -->
|
||||
<!-- Java EE 8 dependency -->
|
||||
<dependency>
|
||||
<groupId>javax</groupId>
|
||||
<artifactId>javaee-api</artifactId>
|
||||
<version>7.0</version>
|
||||
<version>8.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue