Added conversion between spec versions
Added copy builder Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
ad5ab5d491
commit
d54dcf7f83
|
@ -60,4 +60,8 @@ public interface Attributes {
|
||||||
|
|
||||||
Optional<ZonedDateTime> getTime();
|
Optional<ZonedDateTime> getTime();
|
||||||
|
|
||||||
|
Attributes toV03();
|
||||||
|
|
||||||
|
Attributes toV1();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,23 +54,23 @@ public interface CloudEvent {
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getExtensions();
|
Map<String, Object> getExtensions();
|
||||||
|
|
||||||
/**
|
CloudEvent toV03();
|
||||||
* Write an extension into this cloud event
|
|
||||||
* @param e
|
|
||||||
*/
|
|
||||||
default void writeExtension(Extension e) {
|
|
||||||
e.writeToEvent(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static io.cloudevents.v1.CloudEventBuilder build() {
|
CloudEvent toV1();
|
||||||
return buildV1();
|
|
||||||
}
|
|
||||||
|
|
||||||
static io.cloudevents.v1.CloudEventBuilder buildV1() {
|
static io.cloudevents.v1.CloudEventBuilder buildV1() {
|
||||||
return new io.cloudevents.v1.CloudEventBuilder();
|
return new io.cloudevents.v1.CloudEventBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static io.cloudevents.v1.CloudEventBuilder buildV1(CloudEvent event) {
|
||||||
|
return new io.cloudevents.v1.CloudEventBuilder(event);
|
||||||
|
}
|
||||||
|
|
||||||
static io.cloudevents.v03.CloudEventBuilder buildV03() {
|
static io.cloudevents.v03.CloudEventBuilder buildV03() {
|
||||||
return new io.cloudevents.v03.CloudEventBuilder();
|
return new io.cloudevents.v03.CloudEventBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static io.cloudevents.v03.CloudEventBuilder buildV03(CloudEvent event) {
|
||||||
|
return new io.cloudevents.v03.CloudEventBuilder(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package io.cloudevents;
|
package io.cloudevents;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface Extension {
|
public interface Extension {
|
||||||
|
|
||||||
void readFromEvent(CloudEvent event);
|
void readFromEvent(CloudEvent event);
|
||||||
|
|
||||||
void writeToEvent(CloudEvent event);
|
Map<String, Object> asMap();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@ package io.cloudevents.extensions;
|
||||||
import io.cloudevents.CloudEvent;
|
import io.cloudevents.CloudEvent;
|
||||||
import io.cloudevents.Extension;
|
import io.cloudevents.Extension;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class DistributedTracingExtension implements Extension {
|
public final class DistributedTracingExtension implements Extension {
|
||||||
|
|
||||||
public static final String TRACEPARENT = "traceparent";
|
public static final String TRACEPARENT = "traceparent";
|
||||||
|
@ -42,13 +46,11 @@ public final class DistributedTracingExtension implements Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToEvent(CloudEvent event) {
|
public Map<String, Object> asMap() {
|
||||||
if (traceparent != null) {
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
event.getExtensions().put(TRACEPARENT, this.traceparent);
|
map.put(TRACEPARENT, this.traceparent);
|
||||||
}
|
map.put(TRACESTATE, this.tracestate);
|
||||||
if (tracestate != null) {
|
return Collections.unmodifiableMap(map);
|
||||||
event.getExtensions().put(TRACESTATE, this.tracestate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,8 @@ public final class ExtensionsParser {
|
||||||
|
|
||||||
private HashMap<Class<?>, Supplier<Extension>> extensionFactories;
|
private HashMap<Class<?>, Supplier<Extension>> extensionFactories;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO SPI in future?
|
||||||
private ExtensionsParser() {
|
private ExtensionsParser() {
|
||||||
this.extensionFactories = new HashMap<>();
|
this.extensionFactories = new HashMap<>();
|
||||||
registerExtension(DistributedTracingExtension.class, DistributedTracingExtension::new);
|
registerExtension(DistributedTracingExtension.class, DistributedTracingExtension::new);
|
||||||
|
|
|
@ -6,9 +6,7 @@ import io.cloudevents.CloudEvent;
|
||||||
import io.cloudevents.Extension;
|
import io.cloudevents.Extension;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T>, T extends Attributes> {
|
public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T>, T extends Attributes> {
|
||||||
|
@ -18,15 +16,25 @@ public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T
|
||||||
|
|
||||||
private Object data;
|
private Object data;
|
||||||
private Map<String, Object> extensions;
|
private Map<String, Object> extensions;
|
||||||
private List<Extension> materializedExtensions;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public BaseCloudEventBuilder() {
|
public BaseCloudEventBuilder() {
|
||||||
this.self = (B)this;
|
this.self = (B)this;
|
||||||
this.extensions = new HashMap<>();
|
this.extensions = new HashMap<>();
|
||||||
this.materializedExtensions = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public BaseCloudEventBuilder(CloudEvent event) {
|
||||||
|
this.self = (B)this;
|
||||||
|
|
||||||
|
CloudEventImpl ev = (CloudEventImpl) event;
|
||||||
|
this.setAttributes(ev.getAttributes());
|
||||||
|
this.data = ev.getRawData();
|
||||||
|
this.extensions = new HashMap<>(ev.getExtensions());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setAttributes(Attributes attributes);
|
||||||
|
|
||||||
protected abstract B withDataContentType(String contentType);
|
protected abstract B withDataContentType(String contentType);
|
||||||
|
|
||||||
protected abstract B withDataSchema(URI dataSchema);
|
protected abstract B withDataSchema(URI dataSchema);
|
||||||
|
@ -49,15 +57,15 @@ public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T
|
||||||
}
|
}
|
||||||
|
|
||||||
public B withData(String contentType, URI dataSchema, String data) {
|
public B withData(String contentType, URI dataSchema, String data) {
|
||||||
return withEncodedata(contentType, dataSchema, (Object) data);
|
return withEncodeData(contentType, dataSchema, (Object) data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public B withData(String contentType, URI dataSchema, byte[] data) {
|
public B withData(String contentType, URI dataSchema, byte[] data) {
|
||||||
return withEncodedata(contentType, dataSchema, (Object) data);
|
return withEncodeData(contentType, dataSchema, (Object) data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public B withData(String contentType, URI dataSchema, JsonNode data) {
|
public B withData(String contentType, URI dataSchema, JsonNode data) {
|
||||||
return withEncodedata(contentType, dataSchema, (Object) data);
|
return withEncodeData(contentType, dataSchema, (Object) data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public B withExtension(String key, String value) {
|
public B withExtension(String key, String value) {
|
||||||
|
@ -76,19 +84,12 @@ public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T
|
||||||
}
|
}
|
||||||
|
|
||||||
public B withExtension(Extension extension) {
|
public B withExtension(Extension extension) {
|
||||||
this.materializedExtensions.add(extension);
|
this.extensions.putAll(extension.asMap());
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CloudEvent build() {
|
public CloudEvent build() {
|
||||||
CloudEvent event = new CloudEventImpl(this.buildAttributes(), data, extensions);
|
return new CloudEventImpl(this.buildAttributes(), data, extensions);
|
||||||
|
|
||||||
// Write materialized extensions into the event
|
|
||||||
for (Extension ext : this.materializedExtensions) {
|
|
||||||
ext.writeToEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private B withEncodedData(String contentType, Object data) {
|
private B withEncodedData(String contentType, Object data) {
|
||||||
|
@ -97,7 +98,7 @@ public abstract class BaseCloudEventBuilder<B extends BaseCloudEventBuilder<B, T
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
private B withEncodedata(String contentType, URI dataSchema, Object data) {
|
private B withEncodeData(String contentType, URI dataSchema, Object data) {
|
||||||
withDataContentType(contentType);
|
withDataContentType(contentType);
|
||||||
withDataSchema(dataSchema);
|
withDataSchema(dataSchema);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
|
@ -13,14 +13,11 @@ import io.cloudevents.json.Json;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@JsonSerialize(using = CloudEventSerializer.class)
|
@JsonSerialize(using = CloudEventSerializer.class)
|
||||||
@JsonDeserialize(using = CloudEventDeserializer.class)
|
@JsonDeserialize(using = CloudEventDeserializer.class)
|
||||||
public class CloudEventImpl implements CloudEvent {
|
public final class CloudEventImpl implements CloudEvent {
|
||||||
|
|
||||||
private final Attributes attributes;
|
private final Attributes attributes;
|
||||||
private final Object data;
|
private final Object data;
|
||||||
|
@ -109,6 +106,28 @@ public class CloudEventImpl implements CloudEvent {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getExtensions() {
|
public Map<String, Object> getExtensions() {
|
||||||
return extensions;
|
return Collections.unmodifiableMap(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CloudEvent toV03() {
|
||||||
|
return new CloudEventImpl(
|
||||||
|
attributes.toV03(),
|
||||||
|
data,
|
||||||
|
extensions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CloudEvent toV1() {
|
||||||
|
return new CloudEventImpl(
|
||||||
|
attributes.toV1(),
|
||||||
|
data,
|
||||||
|
extensions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getRawData() {
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,21 +32,16 @@ import java.util.Optional;
|
||||||
public class AttributesImpl implements Attributes {
|
public class AttributesImpl implements Attributes {
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
private final URI source;
|
private final URI source;
|
||||||
|
|
||||||
private final String type;
|
private final String type;
|
||||||
|
|
||||||
private final ZonedDateTime time;
|
private final ZonedDateTime time;
|
||||||
private final URI schemaurl;
|
private final URI schemaurl;
|
||||||
|
|
||||||
private final String datacontenttype;
|
private final String datacontenttype;
|
||||||
|
|
||||||
private final String subject;
|
private final String subject;
|
||||||
|
|
||||||
AttributesImpl(String id, URI source, String type,
|
public AttributesImpl(String id, URI source, String type,
|
||||||
ZonedDateTime time, URI schemaurl,
|
ZonedDateTime time, URI schemaurl,
|
||||||
String datacontenttype, String subject) {
|
String datacontenttype, String subject) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -72,6 +67,25 @@ public class AttributesImpl implements Attributes {
|
||||||
public Optional<ZonedDateTime> getTime() {
|
public Optional<ZonedDateTime> getTime() {
|
||||||
return Optional.ofNullable(time);
|
return Optional.ofNullable(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes toV03() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes toV1() {
|
||||||
|
return new io.cloudevents.v1.AttributesImpl(
|
||||||
|
this.id,
|
||||||
|
this.source,
|
||||||
|
this.type,
|
||||||
|
this.datacontenttype,
|
||||||
|
this.schemaurl,
|
||||||
|
this.subject,
|
||||||
|
this.time
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<URI> getDataSchema() {
|
public Optional<URI> getDataSchema() {
|
||||||
return getSchemaUrl();
|
return getSchemaUrl();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package io.cloudevents.v03;
|
package io.cloudevents.v03;
|
||||||
|
|
||||||
|
import io.cloudevents.Attributes;
|
||||||
|
import io.cloudevents.CloudEvent;
|
||||||
import io.cloudevents.impl.BaseCloudEventBuilder;
|
import io.cloudevents.impl.BaseCloudEventBuilder;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -29,20 +31,35 @@ import java.time.ZonedDateTime;
|
||||||
*/
|
*/
|
||||||
public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBuilder, AttributesImpl> {
|
public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBuilder, AttributesImpl> {
|
||||||
|
|
||||||
public CloudEventBuilder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private URI source;
|
private URI source;
|
||||||
|
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
private ZonedDateTime time;
|
private ZonedDateTime time;
|
||||||
private URI schemaurl;
|
private URI schemaurl;
|
||||||
private String datacontenttype;
|
private String datacontenttype;
|
||||||
private String subject;
|
private String subject;
|
||||||
|
|
||||||
|
public CloudEventBuilder() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloudEventBuilder(CloudEvent event) {
|
||||||
|
super(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setAttributes(Attributes attributes) {
|
||||||
|
AttributesImpl attr = (AttributesImpl) attributes.toV03();
|
||||||
|
this
|
||||||
|
.withId(attr.getId())
|
||||||
|
.withSource(attr.getSource())
|
||||||
|
.withType(attr.getType());
|
||||||
|
attr.getDataContentType().ifPresent(this::withDataContentType);
|
||||||
|
attr.getSchemaUrl().ifPresent(this::withSchemaUrl);
|
||||||
|
attr.getSubject().ifPresent(this::withSubject);
|
||||||
|
attr.getTime().ifPresent(this::withTime);
|
||||||
|
}
|
||||||
|
|
||||||
public CloudEventBuilder withId(String id) {
|
public CloudEventBuilder withId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v03.http;
|
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toMap;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import io.cloudevents.fun.BinaryFormatAttributeMapper;
|
|
||||||
import io.cloudevents.v03.ContextAttributes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 0.3
|
|
||||||
*/
|
|
||||||
public class AttributeMapper {
|
|
||||||
private AttributeMapper() {}
|
|
||||||
|
|
||||||
static final String HEADER_PREFIX = "ce-";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Following the signature of {@link BinaryFormatAttributeMapper#map(Map)}
|
|
||||||
* @param headers Map of HTTP request
|
|
||||||
* @return Map with spec attributes and values without parsing
|
|
||||||
* @see ContextAttributes
|
|
||||||
*/
|
|
||||||
public static Map<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v03.http;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import io.cloudevents.fun.FormatExtensionMapper;
|
|
||||||
import io.cloudevents.v03.ContextAttributes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 0.3
|
|
||||||
*/
|
|
||||||
public class ExtensionMapper {
|
|
||||||
private ExtensionMapper() {}
|
|
||||||
|
|
||||||
private static final List<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 -> !RESERVED_HEADERS.contains(header.getKey()))
|
|
||||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v03.http;
|
|
||||||
|
|
||||||
import static io.cloudevents.v03.http.AttributeMapper.HEADER_PREFIX;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import io.cloudevents.fun.FormatHeaderMapper;
|
|
||||||
import io.cloudevents.v03.ContextAttributes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class HeaderMapper {
|
|
||||||
private HeaderMapper() {}
|
|
||||||
|
|
||||||
private static final String HTTP_CONTENT_TYPE = "Content-Type";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Following the signature of {@link FormatHeaderMapper}
|
|
||||||
* @param attributes The map of attributes created by {@link AttributeMapper}
|
|
||||||
* @param extensions The map of extensions created by {@link ExtensionMapper}
|
|
||||||
* @return The map of HTTP Headers
|
|
||||||
*/
|
|
||||||
public static Map<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v03.http;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import io.cloudevents.CloudEvent;
|
|
||||||
import io.cloudevents.extensions.ExtensionFormat;
|
|
||||||
import io.cloudevents.format.BinaryMarshaller;
|
|
||||||
import io.cloudevents.format.StructuredMarshaller;
|
|
||||||
import io.cloudevents.format.Wire;
|
|
||||||
import io.cloudevents.format.builder.EventStep;
|
|
||||||
import io.cloudevents.json.Json;
|
|
||||||
import io.cloudevents.v03.Accessor;
|
|
||||||
import io.cloudevents.v03.AttributesImpl;
|
|
||||||
import io.cloudevents.v03.CloudEventImpl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 0.3
|
|
||||||
*/
|
|
||||||
public class Marshallers {
|
|
||||||
private Marshallers() {}
|
|
||||||
|
|
||||||
private static final Map<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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v03.http;
|
|
||||||
|
|
||||||
import javax.validation.Validator;
|
|
||||||
|
|
||||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
|
||||||
import io.cloudevents.format.BinaryUnmarshaller;
|
|
||||||
import io.cloudevents.format.StructuredUnmarshaller;
|
|
||||||
import io.cloudevents.format.builder.HeadersStep;
|
|
||||||
import io.cloudevents.json.Json;
|
|
||||||
import io.cloudevents.v03.AttributesImpl;
|
|
||||||
import io.cloudevents.v03.CloudEventBuilder;
|
|
||||||
import io.cloudevents.v03.CloudEventImpl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 0.3
|
|
||||||
*/
|
|
||||||
public class Unmarshallers {
|
|
||||||
private Unmarshallers() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
|
||||||
* for HTTP Transport Binding
|
|
||||||
*
|
|
||||||
* @param <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, Validator validator) {
|
|
||||||
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().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, Validator validator) {
|
|
||||||
|
|
||||||
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.withValidator(validator).build();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,17 +31,11 @@ import java.util.Optional;
|
||||||
public class AttributesImpl implements Attributes {
|
public class AttributesImpl implements Attributes {
|
||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
|
|
||||||
private final URI source;
|
private final URI source;
|
||||||
|
|
||||||
private final String type;
|
private final String type;
|
||||||
|
|
||||||
private final String datacontenttype;
|
private final String datacontenttype;
|
||||||
|
|
||||||
private final URI dataschema;
|
private final URI dataschema;
|
||||||
|
|
||||||
private final String subject;
|
private final String subject;
|
||||||
|
|
||||||
private final ZonedDateTime time;
|
private final ZonedDateTime time;
|
||||||
|
|
||||||
public AttributesImpl(String id, URI source,
|
public AttributesImpl(String id, URI source,
|
||||||
|
@ -91,7 +85,25 @@ public class AttributesImpl implements Attributes {
|
||||||
return Optional.ofNullable(time);
|
return Optional.ofNullable(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public Attributes toV03() {
|
||||||
|
return new io.cloudevents.v03.AttributesImpl(
|
||||||
|
this.id,
|
||||||
|
this.source,
|
||||||
|
this.type,
|
||||||
|
this.time,
|
||||||
|
this.dataschema,
|
||||||
|
this.datacontenttype,
|
||||||
|
this.subject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes toV1() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Attibutes V1.0 [id=" + id + ", source=" + source
|
return "Attibutes V1.0 [id=" + id + ", source=" + source
|
||||||
+ ", type=" + type
|
+ ", type=" + type
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.cloudevents.v1;
|
package io.cloudevents.v1;
|
||||||
|
|
||||||
|
import io.cloudevents.Attributes;
|
||||||
|
import io.cloudevents.CloudEvent;
|
||||||
import io.cloudevents.impl.BaseCloudEventBuilder;
|
import io.cloudevents.impl.BaseCloudEventBuilder;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -15,7 +17,6 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private URI source;
|
private URI source;
|
||||||
|
|
||||||
private String type;
|
private String type;
|
||||||
private String datacontenttype;
|
private String datacontenttype;
|
||||||
private URI dataschema;
|
private URI dataschema;
|
||||||
|
@ -26,6 +27,23 @@ public final class CloudEventBuilder extends BaseCloudEventBuilder<CloudEventBui
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CloudEventBuilder(CloudEvent event) {
|
||||||
|
super(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setAttributes(Attributes attributes) {
|
||||||
|
AttributesImpl attr = (AttributesImpl) attributes.toV1();
|
||||||
|
this
|
||||||
|
.withId(attr.getId())
|
||||||
|
.withSource(attr.getSource())
|
||||||
|
.withType(attr.getType());
|
||||||
|
attr.getDataContentType().ifPresent(this::withDataContentType);
|
||||||
|
attr.getDataSchema().ifPresent(this::withDataSchema);
|
||||||
|
attr.getSubject().ifPresent(this::withSubject);
|
||||||
|
attr.getTime().ifPresent(this::withTime);
|
||||||
|
}
|
||||||
|
|
||||||
public CloudEventBuilder withId(String id) {
|
public CloudEventBuilder withId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v1.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.v1.ContextAttributes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package io.cloudevents.v1.http;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import io.cloudevents.fun.FormatExtensionMapper;
|
|
||||||
import io.cloudevents.v1.ContextAttributes;
|
|
||||||
import io.cloudevents.v1.http.AttributeMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class ExtensionMapper {
|
|
||||||
private ExtensionMapper() {}
|
|
||||||
|
|
||||||
private static final List<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 -> !RESERVED_HEADERS.contains(header.getKey()))
|
|
||||||
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v1.http;
|
|
||||||
|
|
||||||
import static io.cloudevents.v1.http.AttributeMapper.HEADER_PREFIX;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import io.cloudevents.fun.FormatHeaderMapper;
|
|
||||||
import io.cloudevents.v1.ContextAttributes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class HeaderMapper {
|
|
||||||
private HeaderMapper() {}
|
|
||||||
|
|
||||||
private static final String HTTP_CONTENT_TYPE = "Content-Type";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Following the signature of {@link FormatHeaderMapper}
|
|
||||||
* @param attributes The map of attributes created by {@link AttributeMapper}
|
|
||||||
* @param extensions The map of extensions created by {@link ExtensionMapper}
|
|
||||||
* @return The map of HTTP Headers
|
|
||||||
*/
|
|
||||||
public static Map<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package io.cloudevents.v1.http;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import io.cloudevents.CloudEvent;
|
|
||||||
import io.cloudevents.extensions.ExtensionFormat;
|
|
||||||
import io.cloudevents.format.BinaryMarshaller;
|
|
||||||
import io.cloudevents.format.StructuredMarshaller;
|
|
||||||
import io.cloudevents.format.Wire;
|
|
||||||
import io.cloudevents.format.builder.EventStep;
|
|
||||||
import io.cloudevents.json.Json;
|
|
||||||
import io.cloudevents.v1.Accessor;
|
|
||||||
import io.cloudevents.v1.AttributesImpl;
|
|
||||||
import io.cloudevents.v1.CloudEventImpl;
|
|
||||||
import io.cloudevents.v1.http.HeaderMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class Marshallers {
|
|
||||||
private Marshallers() {}
|
|
||||||
|
|
||||||
private static final Map<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::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) -> Json.<CloudEvent<AttributesImpl, T>, String>
|
|
||||||
marshaller().marshal(event, NO_HEADERS))
|
|
||||||
.map(Accessor::extensionsOf)
|
|
||||||
.map(ExtensionFormat::marshal)
|
|
||||||
.map(HeaderMapper::map);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright 2019 The CloudEvents Authors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package io.cloudevents.v1.http;
|
|
||||||
|
|
||||||
import javax.validation.Validator;
|
|
||||||
|
|
||||||
import io.cloudevents.extensions.DistributedTracingExtension;
|
|
||||||
import io.cloudevents.format.BinaryUnmarshaller;
|
|
||||||
import io.cloudevents.format.StructuredUnmarshaller;
|
|
||||||
import io.cloudevents.format.builder.HeadersStep;
|
|
||||||
import io.cloudevents.json.Json;
|
|
||||||
import io.cloudevents.v1.AttributesImpl;
|
|
||||||
import io.cloudevents.v1.CloudEventBuilder;
|
|
||||||
import io.cloudevents.v1.CloudEventImpl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author fabiojose
|
|
||||||
* @version 1.0
|
|
||||||
*/
|
|
||||||
public class Unmarshallers {
|
|
||||||
private Unmarshallers() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a Binary Content Mode unmarshaller to unmarshal JSON as CloudEvents data
|
|
||||||
* for HTTP Transport Binding
|
|
||||||
*
|
|
||||||
* @param <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, Validator validator) {
|
|
||||||
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().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, Validator validator) {
|
|
||||||
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.withValidator(validator).build();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,8 +33,7 @@ public class DistributedTracingExtensionTest {
|
||||||
tracing.setTraceparent("parent");
|
tracing.setTraceparent("parent");
|
||||||
tracing.setTracestate("state");
|
tracing.setTracestate("state");
|
||||||
|
|
||||||
CloudEvent event = CloudEvent.build().build();
|
CloudEvent event = CloudEvent.buildV1().withExtension(tracing).build();
|
||||||
tracing.writeToEvent(event);
|
|
||||||
|
|
||||||
assertThat(event.getExtensions())
|
assertThat(event.getExtensions())
|
||||||
.containsEntry(DistributedTracingExtension.TRACEPARENT, "parent")
|
.containsEntry(DistributedTracingExtension.TRACEPARENT, "parent")
|
||||||
|
@ -43,7 +42,7 @@ public class DistributedTracingExtensionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseExtension() {
|
public void parseExtension() {
|
||||||
CloudEvent event = CloudEvent.build()
|
CloudEvent event = CloudEvent.buildV1()
|
||||||
.withExtension(DistributedTracingExtension.TRACEPARENT, "parent")
|
.withExtension(DistributedTracingExtension.TRACEPARENT, "parent")
|
||||||
.withExtension(DistributedTracingExtension.TRACESTATE, "state")
|
.withExtension(DistributedTracingExtension.TRACESTATE, "state")
|
||||||
.build();
|
.build();
|
||||||
|
|
Loading…
Reference in New Issue