Skip to content

Commit

Permalink
EventGrid Beta4 (#18873)
Browse files Browse the repository at this point in the history
  • Loading branch information
YijunXieMS authored Feb 5, 2021
1 parent 6dfa793 commit 815526b
Show file tree
Hide file tree
Showing 28 changed files with 1,533 additions and 1,232 deletions.
363 changes: 216 additions & 147 deletions sdk/eventgrid/azure-messaging-eventgrid/README.md

Large diffs are not rendered by default.

35 changes: 30 additions & 5 deletions sdk/eventgrid/azure-messaging-eventgrid/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,18 @@
<artifactId>azure-core-http-netty</artifactId>
<version>1.7.1</version> <!-- {x-version-update;com.azure:azure-core-http-netty;dependency} -->
</dependency>

<!-- test -->
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-core-serializer-json-jackson</artifactId>
<version>1.1.1</version> <!-- {x-version-update;com.azure:azure-core-serializer-json-jackson;dependency} -->
<scope>test</scope>
</dependency>

<!-- test -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version> <!-- {x-version-update;eventgrid_commons-io:commons-io;external_dependency} -->
<groupId>com.azure</groupId>
<artifactId>azure-storage-queue</artifactId>
<version>12.8.0</version> <!-- {x-version-update;com.azure:azure-storage-queue;dependency} -->
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -121,4 +122,28 @@
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
<profile>
<id>java-lts</id>
<activation>
<jdk>[11,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version> <!-- {x-version-update;org.apache.maven.plugins:maven-surefire-plugin;external_dependency} -->
<configuration>
<argLine>
--add-opens com.azure.messaging.eventgrid/com.azure.messaging.eventgrid=ALL-UNNAMED
--add-opens com.azure.messaging.eventgrid/com.azure.messaging.eventgrid.implementation=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.azure.messaging.eventgrid;

import com.azure.core.util.BinaryData;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.JsonSerializerProviders;
import com.azure.core.util.serializer.TypeReference;

import java.io.ByteArrayInputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
* This is a convenience class that deserializes a json string into events.
* {@link #deserializeCloudEvents(String)} deserializes a JSON string into a list of {@link CloudEvent} instances.
* {@link #deserializeEventGridEvents(String)} deserializes a JSON string into a list of {@link EventGridEvent}
* instances.
*
*/
final class EventGridDeserializer {
private static final ClientLogger LOGGER = new ClientLogger(EventGridDeserializer.class);

private EventGridDeserializer() {
// Hide the constructor
}

static final JsonSerializer DESERIALIZER = JsonSerializerProviders.createInstance();

/**
* Deserialize the {@link EventGridEvent} from a JSON string.
* @param eventGridEventsJson the JSON payload containing one or more events.
*
* @return all of the events in the payload deserialized as {@link EventGridEvent}s.
* @throws IllegalArgumentException if the input parameter isn't a JSON string for a eventgrid event
* or an array of it.
*/
static List<EventGridEvent> deserializeEventGridEvents(String eventGridEventsJson) {
try {
return Arrays.stream(DESERIALIZER
.deserialize(new ByteArrayInputStream(eventGridEventsJson.getBytes(StandardCharsets.UTF_8)),
TypeReference.createInstance(com.azure.messaging.eventgrid.implementation.models.EventGridEvent[].class)))
.map(internalEvent -> {
if (internalEvent.getSubject() == null || internalEvent.getEventType() == null
|| internalEvent.getData() == null) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"'subject', 'type', and 'data' are mandatory attributes for an EventGridEvent. " +
"Check if the input param is a JSON string for an EventGridEvent or an array of it."));
}
return new EventGridEvent(internalEvent);
})
.collect(Collectors.toList());
} catch (UncheckedIOException uncheckedIOException) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException("The input parameter isn't a JSON string.",
uncheckedIOException.getCause()));
}
}

/**
* Deserialize the {@link CloudEvent} from a JSON string.
* @param cloudEventsJson the JSON payload containing one or more events.
*
* @return all of the events in the payload deserialized as {@link CloudEvent}s.
* @throws IllegalArgumentException if the input parameter isn't a JSON string for a cloud event or an array of it.
*/
static List<CloudEvent> deserializeCloudEvents(String cloudEventsJson) {
try {
return Arrays.stream(DESERIALIZER
.deserialize(new ByteArrayInputStream(cloudEventsJson.getBytes(StandardCharsets.UTF_8)),
TypeReference.createInstance(com.azure.messaging.eventgrid.implementation.models.CloudEvent[].class))
)
.map(internalEvent -> {
if (internalEvent.getSource() == null || internalEvent.getType() == null) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException(
"'source' and 'type' are mandatory attributes for a CloudEvent. " +
"Check if the input param is a JSON string for a CloudEvent or an array of it."));
}
return new CloudEvent(internalEvent);
})
.collect(Collectors.toList());
} catch (UncheckedIOException uncheckedIOException) {
throw LOGGER.logExceptionAsError(new IllegalArgumentException("The input parameter isn't a JSON string.",
uncheckedIOException.getCause()));
}
}

static BinaryData getData(Object data) {
if (data == null) {
return null;
}
if (data instanceof byte[]) {
return BinaryData.fromBytes((byte[]) data);
}
if (data instanceof String) {
return BinaryData.fromString((String) data);
}
return BinaryData.fromObject(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,23 @@
package com.azure.messaging.eventgrid;

import com.azure.core.annotation.Fluent;
import com.azure.core.serializer.json.jackson.JacksonJsonSerializerBuilder;
import com.azure.core.util.BinaryData;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.FluxUtil;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.serializer.JacksonAdapter;
import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.TypeReference;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;

/**
* The EventGridEvent model. This represents events in the EventGrid schema to be used with the EventGrid service.
*
* When you send a EventGridEvent to an Event Grid Topic, the topic must be configured to receive the EventGridEvent schema.
*
* For new customers, {@link CloudEvent} is generally preferred over EventGridEvent because the
* <a href="https://docs.microsoft.com/azure/event-grid/cloud-event-schema">CloudEvent schema</a> is supported across
* organizations while the <a href="https://docs.microsoft.com/azure/event-grid/event-schema">EventGridEvent schema</a> is not.
*
* @see EventGridPublisherAsyncClient
* @see EventGridPublisherClient
**/
Expand All @@ -34,27 +31,22 @@ public final class EventGridEvent {

private static final ClientLogger logger = new ClientLogger(EventGridEvent.class);

private boolean parsed = false;

private static final JsonSerializer deserializer = new JacksonJsonSerializerBuilder()
.serializer(new JacksonAdapter().serializer() // this is a workaround to get the FlatteningDeserializer
.registerModule(new JavaTimeModule())) // probably also change this to DateTimeDeserializer when/if it
.build(); // becomes public in core

/**
* Create a new instance of the EventGridEvent, with the given required fields.
* @param subject the subject of the event.
* @param eventType the type of the event, e.g. "Contoso.Items.ItemReceived".
* @param data the data associated with this event.
* @param dataVersion the version of the data sent along with the event.
*
* @throws IllegalArgumentException if subject, eventType or data is {@code null} or empty.
*/
public EventGridEvent(String subject, String eventType, Object data, String dataVersion) {
if (CoreUtils.isNullOrEmpty(subject)) {
throw logger.logExceptionAsError(new IllegalArgumentException("subject cannot be null or empty"));
throw logger.logExceptionAsError(new IllegalArgumentException("'subject' cannot be null or empty."));
} else if (CoreUtils.isNullOrEmpty(eventType)) {
throw logger.logExceptionAsError(new IllegalArgumentException("event type cannot be null or empty"));
throw logger.logExceptionAsError(new IllegalArgumentException("'eventType' cannot be null or empty."));
} else if (CoreUtils.isNullOrEmpty(dataVersion)) {
throw logger.logExceptionAsError(new IllegalArgumentException("data version cannot be null or empty"));
throw logger.logExceptionAsError(new IllegalArgumentException("'dataVersion' cannot be null or empty."));
}

this.event = new com.azure.messaging.eventgrid.implementation.models.EventGridEvent()
Expand All @@ -67,27 +59,16 @@ public EventGridEvent(String subject, String eventType, Object data, String data
}

/**
* Parse the EventGrid Event from a JSON string. This can be used to interpret the event at the event destination
* from raw JSON into rich event(s).
* @param json the JSON payload containing one or more events.
* Deserialize the {@link EventGridEvent} from a JSON string.
* @param eventGridJsonString the JSON payload containing one or more events.
*
* @return all of the events in the payload parsed as CloudEvents.
* @return all of the events in the payload deserialized as {@link EventGridEvent EventGridEvents}.
* @throws IllegalArgumentException if eventGridJsonString isn't a JSON string for a eventgrid event
* or an array of it.
* @throws NullPointerException if eventGridJsonString is {@code null}.
*/
public static List<EventGridEvent> parse(String json) {
return Flux.fromArray(deserializer
.deserialize(new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)),
TypeReference.createInstance(com.azure.messaging.eventgrid.implementation.models.EventGridEvent[].class))
)
.map(event -> {
if (event.getData() == null) {
return new EventGridEvent(event);
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
deserializer.serialize(stream, event.getData());
return new EventGridEvent(event.setData(stream.toByteArray())); // use BinaryData instead?
})
.collectList()
.block();
public static List<EventGridEvent> fromString(String eventGridJsonString) {
return EventGridDeserializer.deserializeEventGridEvents(eventGridJsonString);
}


Expand All @@ -107,7 +88,7 @@ public String getId() {
*/
public EventGridEvent setId(String id) {
if (CoreUtils.isNullOrEmpty(id)) {
throw logger.logExceptionAsError(new IllegalArgumentException("id cannot be null or empty"));
throw logger.logExceptionAsError(new IllegalArgumentException("'id' cannot be null or empty."));
}
this.event.setId(id);
return this;
Expand Down Expand Up @@ -140,87 +121,13 @@ public String getSubject() {
return this.event.getSubject();
}


/**
* Get the data associated with this event. For use in a parsed event only.
* @return If the event was parsed from a Json, this method will return the rich
* system event data if it is a system event, and a {@code byte[]} otherwise, such as in the case of custom event
* data.
* @throws IllegalStateException If the event was not created through {@link EventGridEvent#parse(String)}.
*/
public Object getData() {
if (!parsed) {
// data was set instead of parsed, throw error
throw logger.logExceptionAsError(new IllegalStateException(
"This method should only be called on events created through the parse method"));
}
String eventType = SystemEventMappings.canonicalizeEventType(event.getEventType());
if (SystemEventMappings.getSystemEventMappings().containsKey(eventType)) {
// system event
return deserializer.deserialize(new ByteArrayInputStream((byte[]) this.event.getData()),
TypeReference.createInstance(SystemEventMappings.getSystemEventMappings().get(eventType)));
}
return event.getData();
}

/**
* Get the deserialized data property from the parsed event. The behavior is undefined if this method is called
* on an event that was not created through the parse method.
* @param clazz the class of the type to deserialize the data into.
* @param <T> the type to deserialize the data into.
*
* @return the data deserialized into the given type using a default deserializer.
* @throws IllegalStateException If the event was not created through {@link EventGridEvent#parse(String)}.
*/
public <T> T getData(Class<T> clazz) {
return getDataAsync(clazz, deserializer).block();
}

/**
* Get the deserialized data property from the parsed event.
* @param clazz the class of the type to deserialize the data into.
* @param <T> the type to deserialize the data into.
*
* @return the data deserialized into the given type using a default deserializer, delivered asynchronously through
* a {@link Mono}.
* @throws IllegalStateException If the event was not created through {@link EventGridEvent#parse(String)}.
*/
public <T> Mono<T> getDataAsync(Class<T> clazz) {
return getDataAsync(clazz, deserializer);
}

/**
* Get the deserialized data property from the parsed event.
* @param clazz the class of the type to deserialize the data into.
* @param dataDeserializer the deserializer to use.
* @param <T> the type to deserialize the data into.
*
* @return the data deserialized into the given type using the given deserializer.
* @throws IllegalStateException If the event was not created through {@link EventGridEvent#parse(String)}.
*/
public <T> T getData(Class<T> clazz, JsonSerializer dataDeserializer) {
return getDataAsync(clazz, dataDeserializer).block();
}

/**
* Get the deserialized data property from the parsed event.
* @param clazz the class of the type to deserialize the data into.
* @param dataDeserializer the deserializer to use.
* @param <T> the type to deserialize the data into.
*
* @return the data deserialized into the given type using the given deserializer, delivered asynchronously through
* a {@link Mono}.
* @throws IllegalStateException If the event was not created through {@link EventGridEvent#parse(String)}.
* Get the data associated with this event as a {@link BinaryData}, which has API to deserialize the data into
* a String, an Object, or a byte[].
* @return A {@link BinaryData} that wraps the this event's data payload.
*/
public <T> Mono<T> getDataAsync(Class<T> clazz, JsonSerializer dataDeserializer) {
if (!parsed) {
// data was set instead of parsed, throw exception because we don't know how the data relates to clazz
return FluxUtil.monoError(logger, new IllegalStateException(
"This method should only be called on events created through the parse method"));
}

return dataDeserializer.deserializeAsync(new ByteArrayInputStream((byte[]) this.event.getData()),
TypeReference.createInstance(clazz));
public BinaryData getData() {
return EventGridDeserializer.getData(event.getData());
}

/**
Expand Down Expand Up @@ -259,17 +166,8 @@ public String getDataVersion() {
return this.event.getDataVersion();
}

/**
* Get the metadata version of this event. Note that metadata version is a read-only property set by the service.
* @return the metadata version of this event.
*/
public String getMetadataVersion() {
return this.event.getMetadataVersion();
}

private EventGridEvent(com.azure.messaging.eventgrid.implementation.models.EventGridEvent impl) {
EventGridEvent(com.azure.messaging.eventgrid.implementation.models.EventGridEvent impl) {
this.event = impl;
parsed = true;
}

com.azure.messaging.eventgrid.implementation.models.EventGridEvent toImpl() {
Expand Down
Loading

0 comments on commit 815526b

Please sign in to comment.