Skip to content

Commit

Permalink
Add SDK functionality to make it easier to deserialize/consume EventG…
Browse files Browse the repository at this point in the history
…rid events (#2426)

* Adding support for deserialize/conusme (custom) eventgrid events

* Tests for deserialize/conusme (custom) eventgrid events utility (EventGridSubscriber)

* Adding beta annotation for EventGridSubscriber and associated types, rename mappingExists -> containsMappingFor

* Simplifying code using canonicalizeEventType(String), improved custom mapping lookup methods, adding method to return all custom mapping and adding new IoT & resource events

* Adding tests for new IoT [Connected|Disconnected] & resource [Action*] events

* Rename getAllCustomEventMapping() -> getAllCustomEventMappings(), adding tests for custom mapping operations
  • Loading branch information
anuchandy authored Sep 27, 2018
1 parent 71e50c6 commit 7c1e154
Show file tree
Hide file tree
Showing 39 changed files with 1,820 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
package com.microsoft.azure.eventgrid.customization;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.azure.eventgrid.models.EventGridEvent;
import com.microsoft.azure.management.apigeneration.Beta;
import com.microsoft.azure.serializer.AzureJacksonAdapter;
import com.microsoft.rest.protocol.SerializerAdapter;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* The type that can be used to de-serialize eventgrid events.
*/
@Beta
public class EventGridSubscriber {
/**
* The default adapter to be used for de-serializing the events.
*/
private final AzureJacksonAdapter defaultSerializerAdapter;
/**
* The map containing user defined mapping of eventType to Java model type.
*/
private Map<String, Type> eventTypeToEventDataMapping;

/**
* Creates EventGridSubscriber with default de-serializer.
*/
@Beta
public EventGridSubscriber() {
this.defaultSerializerAdapter = new AzureJacksonAdapter();
this.eventTypeToEventDataMapping = new HashMap<>();
}

/**
* Add a custom event mapping. If a mapping with same eventType exists then the old eventDataType is replaced by
* the specified eventDataType.
*
* @param eventType the event type name.
* @param eventDataType type of the Java model that the event type name mapped to.
*/
@Beta
public void putCustomEventMapping(final String eventType, final Type eventDataType) {
if (eventType == null || eventType.isEmpty()) {
throw new IllegalArgumentException("eventType parameter is required and cannot be null or empty");
}
if (eventDataType == null) {
throw new IllegalArgumentException("eventDataType parameter is required and cannot be null");
}
this.eventTypeToEventDataMapping.put(canonicalizeEventType(eventType), eventDataType);
}

/**
* Get type of the Java model that is mapped to the given eventType.
*
* @param eventType the event type name.
* @return type of the Java model if mapping exists, null otherwise.
*/
@Beta
public Type getCustomEventMapping(final String eventType) {
if (!containsCustomEventMappingFor(eventType)) {
return null;
} else {
return this.eventTypeToEventDataMapping.get(canonicalizeEventType(eventType));
}
}

/**
* @return get all registered custom event mappings.
*/
@Beta
public Set<Map.Entry<String, Type>> getAllCustomEventMappings() {
return Collections.unmodifiableSet(this.eventTypeToEventDataMapping.entrySet());
}

/**
* Removes the mapping with the given eventType.
*
* @param eventType the event type name.
* @return true if the mapping exists and removed, false if mapping does not exists.
*/
@Beta
public boolean removeCustomEventMapping(final String eventType) {
if (!containsCustomEventMappingFor(eventType)) {
return false;
} else {
this.eventTypeToEventDataMapping.remove(canonicalizeEventType(eventType));
return true;
}
}

/**
* Checks if an event mapping with the given eventType exists.
*
* @param eventType the event type name.
* @return true if the mapping exists, false otherwise.
*/
@Beta
public boolean containsCustomEventMappingFor(final String eventType) {
if (eventType == null || eventType.isEmpty()) {
return false;
} else {
return this.eventTypeToEventDataMapping.containsKey(canonicalizeEventType(eventType));
}
}

/**
* De-serialize the events in the given requested content using default de-serializer.
*
* @param requestContent the request content in string format.
* @return De-serialized events.
*
* @throws IOException
*/
@Beta
public EventGridEvent[] deserializeEventGridEvents(final String requestContent) throws IOException {
return this.deserializeEventGridEvents(requestContent, this.defaultSerializerAdapter);
}

/**
* De-serialize the events in the given requested content using the provided de-serializer.
*
* @param requestContent the request content as string.
* @param serializerAdapter the de-serializer.
* @return de-serialized events.
* @throws IOException
*/
@Beta
public EventGridEvent[] deserializeEventGridEvents(final String requestContent, final SerializerAdapter<ObjectMapper> serializerAdapter) throws IOException {
EventGridEvent[] eventGridEvents = serializerAdapter.<EventGridEvent[]>deserialize(requestContent, EventGridEvent[].class);
for (EventGridEvent receivedEvent : eventGridEvents) {
if (receivedEvent.data() == null) {
continue;
} else {
final String eventType = receivedEvent.eventType();
final Type eventDataType;
if (SystemEventTypeMappings.containsMappingFor(eventType)) {
eventDataType = SystemEventTypeMappings.getMapping(eventType);
} else if (containsCustomEventMappingFor(eventType)) {
eventDataType = getCustomEventMapping(eventType);
} else {
eventDataType = null;
}
if (eventDataType != null) {
final String eventDataAsString = serializerAdapter.serializeRaw(receivedEvent.data());
final Object eventData = serializerAdapter.<Object>deserialize(eventDataAsString, eventDataType);
setEventData(receivedEvent, eventData);
}
}
}
return eventGridEvents;
}

private static void setEventData(EventGridEvent event, final Object data) {
// This reflection based way to set the data field needs to be removed once
// we expose a wither in EventGridEvent to set the data. (Check swagger + codegen)
try {
Field dataField = event.getClass().getDeclaredField("data");
dataField.setAccessible(true);
dataField.set(event, data);
} catch (NoSuchFieldException nsfe) {
throw new RuntimeException(nsfe);
} catch (IllegalAccessException iae) {
throw new RuntimeException(iae);
}
}

private static String canonicalizeEventType(final String eventType) {
if (eventType == null) {
return null;
} else {
return eventType.toLowerCase();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
package com.microsoft.azure.eventgrid.customization;

import com.microsoft.azure.eventgrid.models.ContainerRegistryImageDeletedEventData;
import com.microsoft.azure.eventgrid.models.ContainerRegistryImagePushedEventData;
import com.microsoft.azure.eventgrid.models.EventHubCaptureFileCreatedEventData;
import com.microsoft.azure.eventgrid.models.IotHubDeviceConnectedEventData;
import com.microsoft.azure.eventgrid.models.IotHubDeviceCreatedEventData;
import com.microsoft.azure.eventgrid.models.IotHubDeviceDeletedEventData;
import com.microsoft.azure.eventgrid.models.IotHubDeviceDisconnectedEventData;
import com.microsoft.azure.eventgrid.models.MediaJobStateChangeEventData;
import com.microsoft.azure.eventgrid.models.ResourceActionCancelData;
import com.microsoft.azure.eventgrid.models.ResourceActionFailureData;
import com.microsoft.azure.eventgrid.models.ResourceActionSuccessData;
import com.microsoft.azure.eventgrid.models.ResourceDeleteCancelData;
import com.microsoft.azure.eventgrid.models.ResourceDeleteFailureData;
import com.microsoft.azure.eventgrid.models.ResourceDeleteSuccessData;
import com.microsoft.azure.eventgrid.models.ResourceWriteCancelData;
import com.microsoft.azure.eventgrid.models.ResourceWriteFailureData;
import com.microsoft.azure.eventgrid.models.ResourceWriteSuccessData;
import com.microsoft.azure.eventgrid.models.ServiceBusActiveMessagesAvailableWithNoListenersEventData;
import com.microsoft.azure.eventgrid.models.ServiceBusDeadletterMessagesAvailableWithNoListenersEventData;
import com.microsoft.azure.eventgrid.models.StorageBlobCreatedEventData;
import com.microsoft.azure.eventgrid.models.StorageBlobDeletedEventData;
import com.microsoft.azure.eventgrid.models.SubscriptionDeletedEventData;
import com.microsoft.azure.eventgrid.models.SubscriptionValidationEventData;
import com.microsoft.azure.management.apigeneration.Beta;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
* Mapping of system event type name to corresponding type of the Java model.
*/
@Beta
final class SystemEventTypeMappings {
/**
* The map containing system eventType to Java model type mapping.
*/
private static Map<String, Type> systemEventMappings;

static {
systemEventMappings = new HashMap<>(); // key: eventType, Value:eventDataType
//
// ContainerRegistry events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.CONTAINER_REGISTRY_IMAGE_PUSHED_EVENT), ContainerRegistryImagePushedEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.CONTAINER_REGISTRY_IMAGE_DELETED_EVENT), ContainerRegistryImageDeletedEventData.class);
//
// Device events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.IOT_HUB_DEVICE_CREATED_EVENT), IotHubDeviceCreatedEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.IOT_HUB_DEVICE_DELETED_EVENT), IotHubDeviceDeletedEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.IOT_HUB_DEVICE_CONNECTED_EVENT), IotHubDeviceConnectedEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.IOT_HUB_DEVICE_DISCONNECTED_EVENT), IotHubDeviceDisconnectedEventData.class);
//
// EventGrid events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.EVENT_GRID_SUBSCRIPTION_VALIDATION_EVENT), SubscriptionValidationEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.EVENT_GRID_SUBSCRIPTION_DELETED_EVENT), SubscriptionDeletedEventData.class);
//
// Event Hub Events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.EVENT_HUB_CAPTURE_FILE_CREATED_EVENT), EventHubCaptureFileCreatedEventData.class);
//
// Media Services events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.MEDIA_JOB_STATE_CHANGE_EVENT), MediaJobStateChangeEventData.class);
//
// Resource Manager (Azure Subscription/Resource Group) events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_WRITE_SUCCESS_EVENT), ResourceWriteSuccessData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_WRITE_FAILURE_EVENT), ResourceWriteFailureData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_WRITE_CANCEL_EVENT), ResourceWriteCancelData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_DELETE_SUCCESS_EVENT), ResourceDeleteSuccessData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_DELETE_FAILURE_EVENT), ResourceDeleteFailureData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_DELETE_CANCEL_EVENT), ResourceDeleteCancelData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_ACTION_SUCCESS_EVENT), ResourceActionSuccessData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_ACTION_FAILURE_EVENT), ResourceActionFailureData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.RESOURCE_ACTION_CANCEL_EVENT), ResourceActionCancelData.class);
//
// ServiceBus events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.SERVICE_BUS_ACTIVE_MESSAGES_AVAILABLE_WITH_NO_LISTENERS_EVENT), ServiceBusActiveMessagesAvailableWithNoListenersEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.SERVICE_BUS_DEADLETTER_MESSAGES_AVAILABLE_WITH_NO_LISTENER_EVENT), ServiceBusDeadletterMessagesAvailableWithNoListenersEventData.class);
//
// Storage events.
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.STORAGE_BLOB_CREATED_EVENT), StorageBlobCreatedEventData.class);
systemEventMappings.put(canonicalizeEventType(SystemEventTypes.STORAGE_BLOB_DELETED_EVENT), StorageBlobDeletedEventData.class);
}

/**
* Checks if a mapping exists for the given type.
*
* @param eventType the event type.
* @return true if mapping exists, false otherwise.
*/
@Beta
public static boolean containsMappingFor(final String eventType) {
if (eventType == null || eventType.isEmpty()) {
return false;
} else {
return systemEventMappings.containsKey(canonicalizeEventType(eventType));
}
}

/**
* Get Java model type for the given event type.
*
* @param eventType the event type.
* @return the Java model type if mapping exists, null otherwise.
*/
@Beta
public static Type getMapping(final String eventType) {
if (!containsMappingFor(eventType)) {
return null;
} else {
return systemEventMappings.get(canonicalizeEventType(eventType));
}
}

private static String canonicalizeEventType(final String eventType) {
if (eventType == null) {
return null;
} else {
return eventType.toLowerCase();
}
}
}
Loading

0 comments on commit 7c1e154

Please sign in to comment.