Merge pull request #73 from ruromero/validator

Allow using an existing Validator instance
This commit is contained in:
Fabio José 2019-11-28 20:08:46 -03:00 committed by GitHub
commit dfd71734d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 650 additions and 642 deletions

View File

@ -56,8 +56,8 @@
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>${javax.el.version}</version>
<artifactId>jakarta.el</artifactId>
<version>${jakarta.el.version}</version>
</dependency>
<dependency>
@ -79,7 +79,7 @@
<properties>
<jackson.version>2.10.0.pr3</jackson.version>
<hibernate-validator.version>6.0.17.Final</hibernate-validator.version>
<javax.el.version>3.0.1-b11</javax.el.version>
<jakarta.el.version>3.0.3</jakarta.el.version>
</properties>
</project>

View File

@ -15,8 +15,12 @@
*/
package io.cloudevents;
import java.net.URI;
import java.util.Optional;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
@ -27,6 +31,30 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
*/
public interface Attributes {
/**
* @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();
/**
* 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>

View File

@ -42,7 +42,7 @@ public interface ExtensionFormat {
*/
Map<String, String> transport();
public static ExtensionFormat of(final InMemoryFormat inMemory,
static ExtensionFormat of(final InMemoryFormat inMemory,
final String key, final String value) {
final Map<String, String> transport = new HashMap<>();
@ -62,7 +62,7 @@ public interface ExtensionFormat {
}
@SafeVarargs
public static ExtensionFormat of(final InMemoryFormat inMemory,
static ExtensionFormat of(final InMemoryFormat inMemory,
Entry<String, String> ... transport){
Objects.requireNonNull(inMemory);
Objects.requireNonNull(transport);
@ -89,7 +89,7 @@ public interface ExtensionFormat {
* @param extensions
* @return
*/
public static Map<String, String> marshal(Collection<ExtensionFormat>
static Map<String, String> marshal(Collection<ExtensionFormat>
extensions) {
return extensions.stream()

View File

@ -53,7 +53,7 @@ public final class BinaryMarshaller {
return new Builder<A, T, P, H>();
}
public static interface AttributeMarshalStep<A extends Attributes, T, P, H> {
public interface AttributeMarshalStep<A extends Attributes, T, P, H> {
/**
* Marshals the {@link Attributes} instance into a
* {@code Map<String, String>}
@ -63,7 +63,7 @@ public final class BinaryMarshaller {
ExtensionsAccessorStep<A, T, P, H> map(AttributeMarshaller<A> marshaller);
}
public static interface ExtensionsAccessorStep<A extends Attributes, T, P, H> {
public interface ExtensionsAccessorStep<A extends Attributes, T, P, H> {
/**
* To get access of internal collection of {@link ExtensionFormat}
@ -74,7 +74,7 @@ public final class BinaryMarshaller {
}
public static interface ExtensionsStep<A extends Attributes, T, P, H> {
public interface ExtensionsStep<A extends Attributes, T, P, H> {
/**
* Marshals the collection of {@link ExtensionFormat} into a
* {@code Map<String, String>}
@ -84,7 +84,7 @@ public final class BinaryMarshaller {
HeaderMapStep<A, T, P, H> map(ExtensionMarshaller marshaller);
}
public static interface HeaderMapStep<A extends Attributes, T, P, H> {
public interface HeaderMapStep<A extends Attributes, T, P, H> {
/**
* Marshals the map of attributes and extensions into a map of headers
* @param mapper
@ -93,7 +93,7 @@ public final class BinaryMarshaller {
DataMarshallerStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
}
public static interface DataMarshallerStep<A extends Attributes, T, P, H> {
public interface DataMarshallerStep<A extends Attributes, T, P, H> {
/**
* Marshals the 'data' into payload
* @param marshaller
@ -102,7 +102,7 @@ public final class BinaryMarshaller {
BuilderStep<A, T, P, H> map(DataMarshaller<P, T, H> marshaller);
}
public static interface BuilderStep<A extends Attributes, T, P, H> {
public interface BuilderStep<A extends Attributes, T, P, H> {
/**
* Builds the {@link Wire} to use for wire transfer
* @param builder

View File

@ -19,6 +19,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
@ -219,10 +220,8 @@ public final class BinaryUnmarshaller {
A attributes = attributeUnmarshaller.unmarshal(attributesMap);
T data = attributes.getMediaType()
.map((mime) -> {
return dataUnmarshallers.get(mime);
})
.filter((un) -> null != un)
.map((mime) -> dataUnmarshallers.get(mime))
.filter(Objects::nonNull)
.map(unmarshaller ->
unmarshaller.unmarshal(payload, attributes))
.orElse(null);

View File

@ -51,7 +51,7 @@ public class StructuredMarshaller {
return new Builder<>();
}
public static interface MediaTypeStep<A extends Attributes, T, P, H> {
public interface MediaTypeStep<A extends Attributes, T, P, H> {
/**
* Sets the media type of CloudEvents envelope
* @param headerName Example {@code Content-Type} for HTTP
@ -60,7 +60,7 @@ public class StructuredMarshaller {
EnvelopeMarshallerStep<A, T, P, H> mime(String headerName, H mediaType);
}
public static interface EnvelopeMarshallerStep<A extends Attributes, T, P, H> {
public interface EnvelopeMarshallerStep<A extends Attributes, T, P, H> {
/**
* Sets the marshaller for the CloudEvent
* @param marshaller
@ -68,7 +68,7 @@ public class StructuredMarshaller {
ExtensionAccessorStep<A, T, P, H> map(EnvelopeMarshaller<A, T, P> marshaller);
}
public static interface ExtensionAccessorStep<A extends Attributes, T, P, H> {
public interface ExtensionAccessorStep<A extends Attributes, T, P, H> {
/**
* To skip the extension special handling
*/
@ -76,11 +76,11 @@ public class StructuredMarshaller {
ExtensionMarshallerStep<A, T, P, H> map(ExtensionFormatAccessor<A, T> accessor);
}
public static interface ExtensionMarshallerStep<A extends Attributes, T, P, H> {
public 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> {
public interface HeaderMapperStep<A extends Attributes, T, P, H> {
EventStep<A, T, P, H> map(FormatHeaderMapper<H> mapper);
}

View File

@ -144,18 +144,15 @@ public class StructuredUnmarshaller {
Optional.ofNullable(extensionMapper)
.map(mapper -> mapper.map(headers))
.orElse(new HashMap<>());
CloudEvent<A, T> result =
unmarshaller.unmarshal(payload,
() ->
return unmarshaller.unmarshal(payload,
() ->
extensionUnmarshallers.stream()
.map(unmarshaller ->
unmarshaller.unmarshal(extensionsMap))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList()));
return result;
}
}

View File

@ -170,7 +170,7 @@ public class AttributesImpl implements Attributes {
URI schemaurl =
Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name()))
.map(schema -> URI.create(schema))
.map(URI::create)
.orElse(null);
String id = attributes.get(ContextAttributes.id.name());
@ -201,19 +201,11 @@ public class AttributesImpl implements Attributes {
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);
});
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;
}

View File

@ -1,12 +1,12 @@
/**
* Copyright 2019 The CloudEvents Authors
*
* <p>
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
*
* <p>
* 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.
@ -15,8 +15,6 @@
*/
package io.cloudevents.v02;
import static java.lang.String.format;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Collection;
@ -35,242 +33,222 @@ import io.cloudevents.CloudEvent;
import io.cloudevents.extensions.ExtensionFormat;
import io.cloudevents.fun.EventBuilder;
import static java.lang.String.format;
/**
* CloudEvent instances builder
*
*
* @author fabiojose
* @version 0.2
*/
public class CloudEventBuilder<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 = ", ";
private static final String MESSAGE = "'%s' %s";
private static final String ERR_MESSAGE = "invalid payload: %s";
Builder<AttributesImpl, T> {
private CloudEventBuilder() {
}
private String type;
private String id;
private URI source;
private ZonedDateTime time;
private URI schemaurl;
private String contenttype;
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>();
}
/**
*
private static Validator VALIDATOR;
private static final String SPEC_VERSION = "0.2";
private static final String MESSAGE_SEPARATOR = ", ";
private static final String MESSAGE = "'%s' %s";
private static final String ERR_MESSAGE = "invalid payload: %s";
private String type;
private String id;
private URI source;
private ZonedDateTime time;
private URI schemaurl;
private String contenttype;
private T data;
private final Set<ExtensionFormat> extensions = new HashSet<>();
private Validator validator;
private static Validator getValidator() {
if (null == VALIDATOR) {
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
}
return VALIDATOR;
}
/**
* Gets a brand new builder instance
* @param <T> The 'data' type
*/
public static <T> CloudEventBuilder<T> builder() {
return new CloudEventBuilder<>();
}
/**
*
* @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 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());
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()))
.collect(Collectors.joining(MESSAGE_SEPARATOR));
Optional.ofNullable(
"".equals(errs) ? null : errs
).ifPresent((e) -> {
throw new IllegalStateException(format(ERR_MESSAGE, e));
});
return event;
}
public CloudEventBuilder<T> withType(String type) {
this.type = type;
return this;
}
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> withTime(ZonedDateTime time) {
this.time = time;
return this;
}
public CloudEventBuilder<T> withSchemaurl(URI schemaurl) {
this.schemaurl = schemaurl;
return this;
}
public CloudEventBuilder<T> withContenttype(String contenttype) {
this.contenttype = contenttype;
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;
}
public static <T> CloudEventBuilder<T> builder(
CloudEvent<AttributesImpl, T> base) {
/**
* {@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();
}
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(result::withTime);
attributes.getSchemaurl().ifPresent(result::withSchemaurl);
attributes.getContenttype().ifPresent(result::withContenttype);
Accessor.extensionsOf(base).forEach(result::withExtension);
base.getData().ifPresent(result::withData);
return result;
}
/**
*
* @param <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, Validator validator) {
CloudEventBuilder<T> builder = new CloudEventBuilder<T>()
.withId(attributes.getId())
.withSource(attributes.getSource())
.withType(attributes.getType());
attributes.getTime().ifPresent(builder::withTime);
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
attributes.getContenttype().ifPresent(builder::withContenttype);
extensions.forEach(builder::withExtension);
return builder
.withData(data)
.withValidator(validator)
.build();
}
@Override
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
Collection<ExtensionFormat> extensions) {
return CloudEventBuilder.of(data, attributes, extensions, this.validator);
}
/**
* {@inheritDoc}
* @return An new {@link CloudEventImpl} immutable instance
* @throws IllegalStateException When there are specification constraints
* violations
*/
@Override
public CloudEventImpl<T> build() {
AttributesImpl attributes = new AttributesImpl(type, SPEC_VERSION,
source, id, time, schemaurl, contenttype);
CloudEventImpl<T> event = new CloudEventImpl<>(attributes, data, extensions);
if (validator == null) {
validator = getValidator();
}
Set<ConstraintViolation<Object>> violations =
validator.validate(event);
violations.addAll(validator.validate(event.getAttributes()));
final String errs =
violations.stream()
.map(v -> format(MESSAGE, v.getPropertyPath(), v.getMessage()))
.collect(Collectors.joining(MESSAGE_SEPARATOR));
Optional.ofNullable(
"".equals(errs) ? null : errs
).ifPresent((e) -> {
throw new IllegalStateException(format(ERR_MESSAGE, e));
});
return event;
}
public CloudEventBuilder<T> withType(String type) {
this.type = type;
return this;
}
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> withTime(ZonedDateTime time) {
this.time = time;
return this;
}
public CloudEventBuilder<T> withSchemaurl(URI schemaurl) {
this.schemaurl = schemaurl;
return this;
}
public CloudEventBuilder<T> withContenttype(String contenttype) {
this.contenttype = contenttype;
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;
}
public CloudEventBuilder<T> withValidator(Validator validator) {
this.validator = validator;
return this;
}
public <TT> CloudEvent<AttributesImpl, TT>
build(CloudEvent<AttributesImpl, T> base, String id, TT newData) {
return build(base, id, newData, null);
}
public <TT> CloudEvent<AttributesImpl, TT>
build(CloudEvent<AttributesImpl, T> base, String id, TT newData, Validator validator) {
Objects.requireNonNull(base);
AttributesImpl attributes = base.getAttributes();
CloudEventBuilder<TT> builder = new CloudEventBuilder<TT>()
.withId(id)
.withSource(attributes.getSource())
.withType(attributes.getType());
attributes.getTime().ifPresent(builder::withTime);
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
attributes.getContenttype().ifPresent(builder::withContenttype);
Collection<ExtensionFormat> extensions = Accessor.extensionsOf(base);
extensions.forEach(builder::withExtension);
return builder
.withData(newData)
.withValidator(validator)
.build();
}
}

View File

@ -117,7 +117,6 @@ public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
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,

View File

@ -36,8 +36,7 @@ public enum ContextAttributes {
contenttype;
public static final List<String> VALUES =
Arrays.asList(ContextAttributes.values())
.stream()
Arrays.stream(ContextAttributes.values())
.map(Enum::name)
.collect(Collectors.toList());
}

View File

@ -58,9 +58,7 @@ public class ExtensionMapper {
.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());
})
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}

View File

@ -1,5 +1,8 @@
package io.cloudevents.v02.http;
import javax.validation.Valid;
import javax.validation.Validator;
import io.cloudevents.extensions.DistributedTracingExtension;
import io.cloudevents.format.BinaryUnmarshaller;
import io.cloudevents.format.StructuredUnmarshaller;
@ -16,18 +19,33 @@ import io.cloudevents.v02.CloudEventImpl;
*/
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 binary(type, null);
}
/**
* 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
* @param validator Provide an existing instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see BinaryUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
binary(Class<T> type) {
binary(Class<T> type, Validator validator) {
return
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
.map(AttributeMapper::map)
@ -37,20 +55,35 @@ public class Unmarshallers {
.map(ExtensionMapper::map)
.map(DistributedTracingExtension::unmarshall)
.next()
.builder(CloudEventBuilder.<T>builder()::build);
.builder(CloudEventBuilder.<T>builder().withValidator(validator)::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 structured(typeOfData, null);
}
/**
* 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
* @param validator Provided instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see StructuredUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
structured(Class<T> typeOfData) {
structured(Class<T> typeOfData, Validator validator) {
return
StructuredUnmarshaller.<AttributesImpl, T, String>
@ -69,8 +102,8 @@ public class Unmarshallers {
extensions.get().forEach(extension -> {
builder.withExtension(extension);
});
return builder.build();
return builder.withValidator(validator).build();
});
}
}

View File

@ -168,27 +168,13 @@ public class AttributesImpl implements Attributes {
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);
});
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;
}
@ -210,7 +196,7 @@ public class AttributesImpl implements Attributes {
URI schemaurl =
Optional.ofNullable(attributes.get(ContextAttributes.schemaurl.name()))
.map(schema -> URI.create(schema))
.map(URI::create)
.orElse(null);
String id = attributes.get(ContextAttributes.id.name());

View File

@ -36,72 +36,58 @@ import io.cloudevents.fun.EventBuilder;
/**
* The event builder.
*
*
* @author fabiojose
*
*/
public final class CloudEventBuilder<T> implements
public final class CloudEventBuilder<T> implements
EventBuilder<T, AttributesImpl> {
private CloudEventBuilder(Validator validator) {
if(validator == null) {
this.validator = getValidator();
} else {
this.validator = validator;
}
}
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 final Validator validator;
private Validator validator;
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>(null);
return new CloudEventBuilder<>();
}
public static <T> CloudEventBuilder<T> builder(Validator validator) {
return new CloudEventBuilder<T>(validator);
}
public static <T> CloudEventBuilder<T> builder(
CloudEvent<AttributesImpl, T> base) {
return builder(base, null);
}
public static <T> CloudEventBuilder<T> builder(
CloudEvent<AttributesImpl, T> base, Validator validator) {
Objects.requireNonNull(base);
CloudEventBuilder<T> result = new CloudEventBuilder<>(validator);
CloudEventBuilder<T> result = new CloudEventBuilder<>();
AttributesImpl attributes = base.getAttributes();
@ -110,34 +96,13 @@ public final class CloudEventBuilder<T> implements
.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);
});
attributes.getTime().ifPresent(result::withTime);
attributes.getSchemaurl().ifPresent(result::withSchemaurl);
attributes.getDatacontenttype().ifPresent(result::withDatacontenttype);
attributes.getDatacontentencoding().ifPresent(result::withDatacontentencoding);
attributes.getSubject().ifPresent(result::withSubject);
Accessor.extensionsOf(base).forEach(result::withExtension);
base.getData().ifPresent(result::withData);
return result;
}
@ -169,132 +134,124 @@ public final class CloudEventBuilder<T> implements
*/
public static <T> CloudEventImpl<T> of(T data, AttributesImpl attributes,
Collection<ExtensionFormat> extensions, Validator validator) {
CloudEventBuilder<T> builder = CloudEventBuilder.<T>builder(validator)
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();
attributes.getTime().ifPresent(builder::withTime);
attributes.getSchemaurl().ifPresent(builder::withSchemaurl);
attributes.getDatacontentencoding().ifPresent(builder::withDatacontentencoding);
attributes.getDatacontenttype().ifPresent(builder::withDatacontenttype);
attributes.getSubject().ifPresent(builder::withSubject);
extensions.forEach(builder::withExtension);
return builder
.withData(data)
.withValidator(validator)
.build();
}
@Override
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
public CloudEvent<AttributesImpl, T> build(T data, AttributesImpl attributes,
Collection<ExtensionFormat> extensions){
return CloudEventBuilder.<T>of(data, attributes, extensions, null);
return CloudEventBuilder.of(data, attributes, extensions, this.validator);
}
/**
*
*
* @return An new {@link CloudEvent} immutable instance
* @throws IllegalStateException When there are specification constraints
* violations
*/
public CloudEventImpl<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);
CloudEventImpl<T> cloudEvent =
new CloudEventImpl<>(attributes, data, extensions);
if(validator == null) {
validator = getValidator();
}
Set<ConstraintViolation<Object>> violations =
validator.validate(cloudEvent);
violations.addAll(validator.validate(cloudEvent.getAttributes()));
final String errs =
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;
}
public CloudEventBuilder<T> withValidator(Validator validator) {
this.validator = validator;
return this;
}
}

View File

@ -114,7 +114,6 @@ public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
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,

View File

@ -38,8 +38,7 @@ public enum ContextAttributes {
subject;
public static final List<String> VALUES =
Arrays.asList(ContextAttributes.values())
.stream()
Arrays.stream(ContextAttributes.values())
.map(Enum::name)
.collect(Collectors.toList());
}

View File

@ -58,9 +58,7 @@ public class ExtensionMapper {
.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());
})
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}

View File

@ -15,6 +15,8 @@
*/
package io.cloudevents.v03.http;
import javax.validation.Validator;
import io.cloudevents.extensions.DistributedTracingExtension;
import io.cloudevents.format.BinaryUnmarshaller;
import io.cloudevents.format.StructuredUnmarshaller;
@ -31,18 +33,33 @@ import io.cloudevents.v03.CloudEventImpl;
*/
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 binary(type, null);
}
/**
* 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
* @param validator Provided instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see BinaryUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
binary(Class<T> type) {
binary(Class<T> type, Validator validator) {
return
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
.map(AttributeMapper::map)
@ -52,20 +69,35 @@ public class Unmarshallers {
.map(ExtensionMapper::map)
.map(DistributedTracingExtension::unmarshall)
.next()
.builder(CloudEventBuilder.<T>builder()::build);
.builder(CloudEventBuilder.<T>builder().withValidator(validator)::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 structured(typeOfData, null);
}
/**
* 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
* @param validator Provided instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see StructuredUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
structured(Class<T> typeOfData) {
structured(Class<T> typeOfData, Validator validator) {
return
StructuredUnmarshaller.<AttributesImpl, T, String>
@ -85,7 +117,7 @@ public class Unmarshallers {
builder.withExtension(extension);
});
return builder.build();
return builder.withValidator(validator).build();
});
}
}

View File

@ -165,23 +165,12 @@ public class AttributesImpl implements Attributes {
result.put(ContextAttributes.type.name(),
attributes.getType());
attributes.getDatacontenttype().ifPresent(dct -> {
result.put(ContextAttributes.datacontenttype.name(), dct);
});
attributes.getDataschema().ifPresent(dataschema -> {
result.put(ContextAttributes.dataschema.name(),
dataschema.toString());
});
attributes.getSubject().ifPresent(subject -> {
result.put(ContextAttributes.subject.name(), subject);
});
attributes.getTime().ifPresent(time -> {
result.put(ContextAttributes.time.name(),
time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
});
attributes.getDatacontenttype().ifPresent(dct -> result.put(ContextAttributes.datacontenttype.name(), dct));
attributes.getDataschema().ifPresent(dataschema -> result.put(ContextAttributes.dataschema.name(),
dataschema.toString()));
attributes.getSubject().ifPresent(subject -> result.put(ContextAttributes.subject.name(), subject));
attributes.getTime().ifPresent(time -> result.put(ContextAttributes.time.name(),
time.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)));
return result;
}
@ -203,7 +192,7 @@ public class AttributesImpl implements Attributes {
URI dataschema =
Optional.ofNullable(attributes.get(ContextAttributes.dataschema.name()))
.map(schema -> URI.create(schema))
.map(URI::create)
.orElse(null);
String id = attributes.get(ContextAttributes.id.name());

View File

@ -44,11 +44,13 @@ public class CloudEventBuilder<T> implements
private URI dataschema;
private String subject;
private ZonedDateTime time;
private T data;
private final Set<ExtensionFormat> extensions = new HashSet<>();
private Validator validator;
private static Validator getValidator() {
if(null== VALIDATOR) {
VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
@ -61,16 +63,16 @@ public class CloudEventBuilder<T> implements
* @param <T> The 'data' type
*/
public static <T> CloudEventBuilder<T> builder() {
return new CloudEventBuilder<T>();
return new CloudEventBuilder<>();
}
/**
* Builder with base event to copy attributes
* @param <T> The 'data' type
* @param base The base event to copy attributes
*/
public static <T> CloudEventBuilder<T> builder(
CloudEvent<AttributesImpl, T> base) {
CloudEvent<AttributesImpl, T> base) {
Objects.requireNonNull(base);
CloudEventBuilder<T> result = new CloudEventBuilder<>();
@ -82,31 +84,13 @@ public class CloudEventBuilder<T> implements
.withSource(attributes.getSource())
.withType(attributes.getType());
attributes.getDataschema().ifPresent((schema) -> {
result.withDataschema(schema);
});
attributes.getDatacontenttype().ifPresent(dc -> {
result.withDataContentType(dc);
});
attributes.getSubject().ifPresent(subject -> {
result.withSubject(subject);
});
attributes.getTime().ifPresent(time -> {
result.withTime(time);
});
Accessor.extensionsOf(base)
.forEach(extension -> {
result.withExtension(extension);
});
base.getData().ifPresent(data -> {
result.withData(data);
});
attributes.getDataschema().ifPresent(result::withDataschema);
attributes.getDatacontenttype().ifPresent(result::withDataContentType);
attributes.getSubject().ifPresent(result::withSubject);
attributes.getTime().ifPresent(result::withTime);
Accessor.extensionsOf(base).forEach(result::withExtension);
base.getData().ifPresent(result::withData);
return result;
}
@ -120,30 +104,16 @@ public class CloudEventBuilder<T> implements
.withSource(attributes.getSource())
.withType(attributes.getType());
attributes.getTime().ifPresent((time) -> {
builder.withTime(time);
});
attributes.getDataschema().ifPresent((dataschema) -> {
builder.withDataschema(dataschema);
});
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();
attributes.getTime().ifPresent(builder::withTime);
attributes.getDataschema().ifPresent(builder::withDataschema);
attributes.getDatacontenttype().ifPresent(builder::withDataContentType);
attributes.getSubject().ifPresent(builder::withSubject);
extensions.forEach(builder::withExtension);
return builder
.withData(data)
.withValidator(validator)
.build();
}
/**
@ -157,17 +127,19 @@ public class CloudEventBuilder<T> implements
AttributesImpl attributes = new AttributesImpl(id, source, SPEC_VERSION, type,
datacontenttype, dataschema, subject, time);
CloudEventImpl<T> cloudEvent =
new CloudEventImpl<T>(attributes, data, extensions);
CloudEventImpl<T> cloudEvent =
new CloudEventImpl<>(attributes, data, extensions);
if(data instanceof byte[]) {
cloudEvent.setDataBase64((byte[])data);
}
if(validator == null) {
validator = getValidator();
}
Set<ConstraintViolation<Object>> violations =
getValidator().validate(cloudEvent);
validator.validate(cloudEvent);
violations.addAll(getValidator().validate(cloudEvent.getAttributes()));
violations.addAll(validator.validate(cloudEvent.getAttributes()));
final String errs =
violations.stream()
@ -231,4 +203,9 @@ public class CloudEventBuilder<T> implements
this.extensions.add(extension);
return this;
}
public CloudEventBuilder<T> withValidator(Validator validator) {
this.validator = validator;
return this;
}
}

View File

@ -140,7 +140,6 @@ public class CloudEventImpl<T> implements CloudEvent<AttributesImpl, T> {
public static <T> CloudEventImpl<T> build(
@JsonProperty("id") String id,
@JsonProperty("source") URI source,
@JsonProperty("specversion") String specversion,
@JsonProperty("type") String type,
@JsonProperty("datacontenttype") String datacontenttype,
@JsonProperty("dataschema") URI dataschema,

View File

@ -36,8 +36,7 @@ public enum ContextAttributes {
time;
public static final List<String> VALUES =
Arrays.asList(ContextAttributes.values())
.stream()
Arrays.stream(ContextAttributes.values())
.map(Enum::name)
.collect(Collectors.toList());
}

View File

@ -44,9 +44,7 @@ public class ExtensionMapper {
.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());
})
.filter(header -> !RESERVED_HEADERS.contains(header.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
}

View File

@ -58,14 +58,14 @@ public class HeaderMapper {
.toLowerCase(Locale.US), header.getValue()))
.filter(header -> !header.getKey()
.equals(ContextAttributes.datacontenttype.name()))
.map(header -> new SimpleEntry<>(HEADER_PREFIX+header.getKey(),
.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())
.filter(extension -> null != extension.getValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))
);

View File

@ -15,6 +15,8 @@
*/
package io.cloudevents.v1.http;
import javax.validation.Validator;
import io.cloudevents.extensions.DistributedTracingExtension;
import io.cloudevents.format.BinaryUnmarshaller;
import io.cloudevents.format.StructuredUnmarshaller;
@ -23,8 +25,6 @@ import io.cloudevents.json.Json;
import io.cloudevents.v1.AttributesImpl;
import io.cloudevents.v1.CloudEventBuilder;
import io.cloudevents.v1.CloudEventImpl;
import io.cloudevents.v1.http.AttributeMapper;
import io.cloudevents.v1.http.ExtensionMapper;
/**
*
@ -33,18 +33,33 @@ import io.cloudevents.v1.http.ExtensionMapper;
*/
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>
public static <T> HeadersStep<AttributesImpl, T, String>
binary(Class<T> type) {
return binary(type, null);
}
/**
* 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
* @param validator Provided instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see BinaryUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
binary(Class<T> type, Validator validator) {
return
BinaryUnmarshaller.<AttributesImpl, T, String>builder()
.map(AttributeMapper::map)
@ -54,7 +69,7 @@ public class Unmarshallers {
.map(ExtensionMapper::map)
.map(DistributedTracingExtension::unmarshall)
.next()
.builder(CloudEventBuilder.<T>builder()::build);
.builder(CloudEventBuilder.<T>builder().withValidator(validator)::build);
}
/**
@ -68,7 +83,21 @@ public class Unmarshallers {
*/
public static <T> HeadersStep<AttributesImpl, T, String>
structured(Class<T> typeOfData) {
return structured(typeOfData, null);
}
/**
* 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
* @param validator Provided instance of a {@link Validator}
* @return A step to supply the headers, payload and to unmarshal
* @see StructuredUnmarshaller
*/
public static <T> HeadersStep<AttributesImpl, T, String>
structured(Class<T> typeOfData, Validator validator) {
return
StructuredUnmarshaller.<AttributesImpl, T, String>
builder()
@ -86,8 +115,8 @@ public class Unmarshallers {
extensions.get().forEach(extension -> {
builder.withExtension(extension);
});
return builder.build();
return builder.withValidator(validator).build();
});
}
}

View File

@ -78,10 +78,7 @@ public class StructuredMarshallerTest {
StructuredMarshaller.<Attributes, Much, String, String>builder()
.mime("Content-Type", "application/cloudevents+json")
.map((ce) -> {
return null;
})
.map((ce) -> null)
.skip()
.withEvent(null);
}
@ -91,10 +88,7 @@ public class StructuredMarshallerTest {
// act
StructuredMarshaller.<Attributes, Much, String, String>builder()
.mime("Content-Type", "application/cloudevents+json")
.map((ce) -> {
return null;
})
.map((ce) -> null)
.skip()
.withEvent(() -> null);
}
@ -106,10 +100,7 @@ public class StructuredMarshallerTest {
StructuredMarshaller.<Attributes, Much, String, String>builder()
.mime("Content-Type", "application/cloudevents+json")
.map((ce) -> {
return null;
})
.map((ce) -> null)
.map(null);
}
@ -118,14 +109,8 @@ public class StructuredMarshallerTest {
// act
StructuredMarshaller.<Attributes, Much, String, String>builder()
.mime("Content-Type", "application/cloudevents+json")
.map((ce) -> {
return null;
})
.map((event) -> {
return null;
});
.map((ce) -> null)
.map((event) -> null);
}
@Test
@ -135,14 +120,8 @@ public class StructuredMarshallerTest {
StructuredMarshaller.<Attributes, Much, String, String>builder()
.mime("Content-Type", "application/cloudevents+json")
.map((ce) -> {
return null;
})
.map((event) -> {
return null;
})
.map((ce) -> null)
.map((event) -> null)
.map(null);
}
@ -150,18 +129,9 @@ public class StructuredMarshallerTest {
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;
});
.map((ce) -> null)
.map((event) -> null)
.map((extensions) -> null);
}
@Test
@ -171,18 +141,9 @@ public class StructuredMarshallerTest {
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((ce) -> null)
.map((event) -> null)
.map((extensions) -> null)
.map(null);
}
@ -190,18 +151,9 @@ public class StructuredMarshallerTest {
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((ce) -> null)
.map((event) -> null)
.map((extensions) -> null)
.map((attributes, extensions) -> null);
}
}

View File

@ -18,10 +18,15 @@ package io.cloudevents.v02;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Collections;
import javax.validation.Validator;
import io.cloudevents.validation.MockValidator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -318,4 +323,39 @@ public class CloudEventBuilderTest {
assertEquals(expected, actual.getData().get());
assertEquals("0x010", actual.getAttributes().getId());
}
@Test
public void should_build_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEventImpl<String> event = CloudEventBuilder
.<String>builder()
.withData(expected)
.withValidator(validator)
.build();
assertNotNull(event);
assertEquals(expected, event.getData().get());
}
@Test
public void should_build_event_from_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEvent<AttributesImpl, String> event = CloudEventBuilder.of(
expected,
new AttributesImpl(null, null, null, null, null, null, null),
Collections.emptyList(),
validator
);
CloudEvent<AttributesImpl, String> result = CloudEventBuilder
.builder(event)
.withValidator(validator)
.build();
assertNotNull(result);
assertEquals(expected, result.getData().get());
}
}

View File

@ -33,6 +33,7 @@ import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import javax.validation.metadata.BeanDescriptor;
import io.cloudevents.validation.MockValidator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -217,33 +218,15 @@ public class CloudEventBuilderTest {
assertTrue(actual instanceof String);
}
@Test
public void should_copy_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEvent<AttributesImpl, String> event = CloudEventBuilder.<String>of(
expected,
new AttributesImpl(null, null, null, null, null, null, null, null, null),
Collections.emptyList(),
validator
);
try {
CloudEventBuilder.<String>builder().build();
fail("Expected validation error");
} catch (IllegalStateException e) {
assertNotNull(e);
}
}
@Test
public void should_build_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEventImpl<String> event = CloudEventBuilder
.<String>builder(validator)
.<String>builder()
.withData(expected)
.withValidator(validator)
.build();
assertNotNull(event);
@ -262,43 +245,11 @@ public class CloudEventBuilderTest {
);
CloudEvent<AttributesImpl, String> result = CloudEventBuilder
.<String>builder(event, validator)
.builder(event)
.withValidator(validator)
.build();
assertNotNull(result);
assertEquals(expected, result.getData().get());
}
private static class MockValidator implements Validator {
@Override
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
return new HashSet<>();
}
@Override
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
return null;
}
@Override
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
return null;
}
@Override
public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
return null;
}
@Override
public <T> T unwrap(Class<T> type) {
return null;
}
@Override
public ExecutableValidator forExecutables() {
return null;
}
}
}

View File

@ -18,9 +18,14 @@ package io.cloudevents.v1;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.net.URI;
import java.util.Collections;
import javax.validation.Validator;
import io.cloudevents.validation.MockValidator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@ -191,4 +196,37 @@ public class CloudEventBuilderTest {
assertNotNull(actual);
assertTrue(actual instanceof String);
}
@Test
public void should_build_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEventImpl<String> event = CloudEventBuilder
.<String>builder()
.withData(expected)
.withValidator(validator)
.build();
assertNotNull(event);
assertEquals(expected, event.getData().get());
}
@Test
public void should_build_event_from_event_using_custom_validator() {
Validator validator = new MockValidator();
String expected = "test";
CloudEvent<AttributesImpl, String> event = CloudEventBuilder.<String>builder()
.withData(expected)
.withValidator(validator)
.build();
CloudEvent<AttributesImpl, String> result = CloudEventBuilder
.builder(event)
.withValidator(validator)
.build();
assertNotNull(result);
assertEquals(expected, result.getData().get());
}
}

View File

@ -0,0 +1,42 @@
package io.cloudevents.validation;
import java.util.HashSet;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import javax.validation.metadata.BeanDescriptor;
public class MockValidator implements Validator {
@Override
public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
return new HashSet<>();
}
@Override
public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
return null;
}
@Override
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
return null;
}
@Override
public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
return null;
}
@Override
public <T> T unwrap(Class<T> type) {
return null;
}
@Override
public ExecutableValidator forExecutables() {
return null;
}
}