Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework the Event interface #108

Merged
merged 21 commits into from
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 16 additions & 24 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,43 +23,41 @@
<version>1.3.0</version>
</parent>

<groupId>io.cloudevents</groupId>
<artifactId>cloudevents-api</artifactId>
<name>CloudEvents - API</name>
<version>1.3.0</version>
<version>${parent.version}</version>
<packaging>jar</packaging>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>${jackson.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>

<!-- Jackson Module for Option<> -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>${jackson.version}</version>
</dependency>

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>

<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>${jakarta.el.version}</version>
</dependency>

<!-- Test deps -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand All @@ -76,10 +74,4 @@

</dependencies>

<properties>
<jackson.version>2.10.1</jackson.version>
<hibernate-validator.version>6.0.17.Final</hibernate-validator.version>
<jakarta.el.version>3.0.3</jakarta.el.version>
</properties>

</project>
34 changes: 16 additions & 18 deletions api/src/main/java/io/cloudevents/Attributes.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,50 +16,48 @@
package io.cloudevents;

import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Optional;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
* The marker interface for CloudEvents attributes
*
*
* @author fabiojose
*
*/
public interface Attributes {

/**
* @return The version of the CloudEvents specification which the event uses
*/
SpecVersion getSpecVersion();

/**
* @return Identifies the event. Producers MUST ensure that source + id is unique for each distinct event
*/
@NotBlank
String getId();

/**
* @return A value describing the type of event related to the originating occurrence.
*/
@NotBlank
String getType();

/**
* @return The context in which an event happened.
*/
@NotNull
URI getSource();

/**
* @return The version of the CloudEvents specification which the event uses
*/
@NotBlank
String getSpecversion();

/**
* TODO
* 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();

Optional<String> getDataContentType();

Optional<URI> getDataSchema();
slinkydeveloper marked this conversation as resolved.
Show resolved Hide resolved

Optional<String> getSubject();

Optional<ZonedDateTime> getTime();

}
39 changes: 0 additions & 39 deletions api/src/main/java/io/cloudevents/Builder.java

This file was deleted.

35 changes: 28 additions & 7 deletions api/src/main/java/io/cloudevents/CloudEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,52 @@
*/
package io.cloudevents;

import io.cloudevents.v03.CloudEventBuilder;

import java.util.Map;
import java.util.Optional;

/**
* An abstract event envelope
* @param <A> The attributes type
* @param <T> The 'data' type
* @author fabiojose
* @author slinkydeveloper
*/
public interface CloudEvent<A extends Attributes, T> {
public interface CloudEvent {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this is a good change. too many generics makes this really hard to use.


/**
* The event context attributes
*/
A getAttributes();
Attributes getAttributes();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI #118

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@slinkydeveloper slinkydeveloper Apr 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the solutions here are two:

  • CloudEvent is implemented once (impl.CloudEventImpl) and different attributes versions are added through composition of objects.
  • CloudEvent is implemented multiple times, one for each event version.

The reason why i went with the first alternative is to simplify the implementation of json marshal/unmarshal and because i'm more a composition guy, but let's review this comment later when this pr is ready.


/**
* The event data
*/
Optional<T> getData();

byte[] getDataBase64();
Optional<Data> getData();

/**
* The event extensions
*
* Extensions values could be String/Number/Boolean/JsonNode
*/
Map<String, Object> getExtensions();

/**
* Write an extension into this cloud event
* @param e
*/
default void writeExtension(Extension e) {
slinkydeveloper marked this conversation as resolved.
Show resolved Hide resolved
e.writeToEvent(this);
}

static io.cloudevents.v1.CloudEventBuilder build() {
slinkydeveloper marked this conversation as resolved.
Show resolved Hide resolved
return buildV1();
}

static io.cloudevents.v1.CloudEventBuilder buildV1() {
return new io.cloudevents.v1.CloudEventBuilder();
}

static io.cloudevents.v03.CloudEventBuilder buildV03() {
return new io.cloudevents.v03.CloudEventBuilder();
}
}
43 changes: 43 additions & 0 deletions api/src/main/java/io/cloudevents/Data.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.cloudevents;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.cloudevents.common.BinaryData;
import io.cloudevents.common.JsonData;
import io.cloudevents.common.StringData;
import io.cloudevents.json.Json;

/**
* TODO
*
* @author slinkydeveloper
*/
public interface Data {

byte[] asBinary() throws DataConversionException;

String asString() throws DataConversionException;

JsonNode asJson() throws DataConversionException;

static Data newJson(JsonNode json) {
return new JsonData(json);
}

static Data newJson(Object json) {
try {
return Json.MAPPER.valueToTree(json);
} catch (IllegalArgumentException e) {
throw new DataConversionException(json.getClass().toString(), "JsonNode", e);
}
}

static Data newBinary(byte[] binary) {
return new BinaryData(binary);
}

static Data newString(String string) {
return new StringData(string);
}

}
8 changes: 8 additions & 0 deletions api/src/main/java/io/cloudevents/DataConversionException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.cloudevents;

public class DataConversionException extends RuntimeException {

public DataConversionException(String from, String to, Throwable cause) {
slinkydeveloper marked this conversation as resolved.
Show resolved Hide resolved
super("Cannot convert " + from + " data to " + to, cause);
}
}
9 changes: 9 additions & 0 deletions api/src/main/java/io/cloudevents/Extension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.cloudevents;

public interface Extension {

void readFromEvent(CloudEvent event);

void writeToEvent(CloudEvent event);

}
6 changes: 6 additions & 0 deletions api/src/main/java/io/cloudevents/SpecVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.cloudevents;

public enum SpecVersion {
V03,
V1
}
71 changes: 71 additions & 0 deletions api/src/main/java/io/cloudevents/common/BaseCloudEventBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.cloudevents.common;

import io.cloudevents.Attributes;
import io.cloudevents.CloudEvent;
import io.cloudevents.Data;
import io.cloudevents.Extension;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T>, T extends Attributes> {

// This is a little trick for enabling fluency
private B self;

private Data data;
private Map<String, Object> extensions;
private List<Extension> materializedExtensions;


@SuppressWarnings("unchecked")
public BaseCloudEventBuilder() {
this.self = (B)this;
this.extensions = new HashMap<>();
this.materializedExtensions = new ArrayList<>();
}

protected abstract B withDataContentType(String contentType);

protected abstract B withDataSchema(URI dataSchema);

protected abstract T buildAttributes();

public B withData(String contentType, Data data) {
withDataContentType(contentType);
this.data = data;
return self;
}

public B withData(String contentType, URI dataSchema, Data data) {
withDataContentType(contentType);
withDataSchema(dataSchema);
this.data = data;
return self;
}

public B withExtension(String key, Object value) {
this.extensions.put(key, value);
return self;
}

public B withExtension(Extension extension) {
this.materializedExtensions.add(extension);
return self;
}

public CloudEvent build() {
CloudEvent event = new CloudEventImpl(this.buildAttributes(), data, extensions);

for (Extension ext : this.materializedExtensions) {
ext.writeToEvent(event);
}

return event;
}

}
Loading