Skip to content

Commit

Permalink
EG API updates (#18274)
Browse files Browse the repository at this point in the history
* EG API updates

* webjob tests

* Use optional serializer param in GetData<T> methods; revert culture in SAS
  • Loading branch information
JoshLove-msft authored Jan 29, 2021
1 parent 12a86ca commit 2a0bbe9
Show file tree
Hide file tree
Showing 18 changed files with 478 additions and 439 deletions.
10 changes: 5 additions & 5 deletions sdk/eventgrid/Azure.Messaging.EventGrid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ Publishing events to Event Grid is performed using the `EventGridPublisherClient
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
};

// Send the events
Expand Down Expand Up @@ -138,10 +138,10 @@ To publish events to any topic in an Event Domain, push the events to the domain
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
{
Topic = "MyTopic"
}
Expand Down Expand Up @@ -185,7 +185,7 @@ foreach (CloudEvent cloudEvent in cloudEvents)
TestPayload testPayload = await cloudEvent.GetDataAsync<TestPayload>(myCustomSerializer);
Console.WriteLine(testPayload.Name);
break;
case SystemEventMappings.StorageBlobDeletedEventName:
case SystemEventNames.StorageBlobDeleted:
// Example for deserializing system events using GetData<T>
StorageBlobDeletedEventData blobDeleted = cloudEvent.GetData<StorageBlobDeletedEventData>();
Console.WriteLine(blobDeleted.BlobType);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ Note on `EventGridEvent`: each `EventGridEvent` has a set of required, non-nulla
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
};

// Send the events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ To publish events to any topic in an Event Domain, push the events to the domain
List<EventGridEvent> eventsList = new List<EventGridEvent>
{
new EventGridEvent(
"This is the event data",
"ExampleEventSubject",
"Example.EventType",
"1.0")
"1.0",
"This is the event data")
{
Topic = "MyTopic"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ foreach (CloudEvent cloudEvent in cloudEvents)
TestPayload testPayload = await cloudEvent.GetDataAsync<TestPayload>(myCustomSerializer);
Console.WriteLine(testPayload.Name);
break;
case SystemEventMappings.StorageBlobDeletedEventName:
case SystemEventNames.StorageBlobDeleted:
// Example for deserializing system events using GetData<T>
StorageBlobDeletedEventData blobDeleted = cloudEvent.GetData<StorageBlobDeletedEventData>();
Console.WriteLine(blobDeleted.BlobType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@
<Compile Include="$(AzureCoreSharedSources)Argument.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
<Compile Include="$(AzureCoreSharedSources)AzureResourceProviderNamespaceAttribute.cs" Link="Shared\%(RecursiveDir)\%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Azure.Core\src\Azure.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -167,51 +167,42 @@ public static CloudEvent[] Parse(string requestContent)
/// Deserializes the event payload into a specified event type using the provided <see cref="ObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="serializer"> Custom serializer used to deserialize the payload. </param>
/// <param name="serializer"> A custom serializer used to deserialize the payload. If not provided, the
/// <see cref="JsonObjectSerializer"/> will be used.</param>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from CloudEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public async Task<T> GetDataAsync<T>(ObjectSerializer serializer, CancellationToken cancellationToken = default)
public async Task<T> GetDataAsync<T>(ObjectSerializer serializer = default, CancellationToken cancellationToken = default)
{
if (Data != null)
if (Data != null && serializer != null)
{
throw new InvalidOperationException("Cannot pass in a custom deserializer if event was not created from CloudEvent.Parse(), " +
"as event data should already be deserialized and the custom deserializer will not be used.");
}
return await GetDataInternal<T>(serializer, true, cancellationToken).ConfigureAwait(false);
return await GetDataInternal<T>(serializer ?? s_jsonSerializer, true, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Deserializes the event payload into a specified event type using the provided <see cref="ObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="serializer"> Custom serializer used to deserialize the payload. </param>
/// <param name="serializer"> A custom serializer used to deserialize the payload. If not provided, the
/// <see cref="JsonObjectSerializer"/> will be used.</param>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from CloudEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public T GetData<T>(ObjectSerializer serializer, CancellationToken cancellationToken = default)
public T GetData<T>(ObjectSerializer serializer = default, CancellationToken cancellationToken = default)
{
if (Data != null)
if (Data != null && serializer != null)
{
throw new InvalidOperationException("Cannot pass in a custom deserializer if event was not created from CloudEvent.Parse(), " +
"as event data should already be deserialized and the custom deserializer will not be used.");
}
return GetDataInternal<T>(serializer, false, cancellationToken).EnsureCompleted();
return GetDataInternal<T>(serializer ?? s_jsonSerializer, false, cancellationToken).EnsureCompleted();
}

/// <summary>
/// Deserializes the event payload into a specified event type using the provided <see cref="JsonObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from CloudEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public T GetData<T>(CancellationToken cancellationToken = default)
=> GetDataInternal<T>(s_jsonSerializer, false, cancellationToken).EnsureCompleted();

private async Task<T> GetDataInternal<T>(ObjectSerializer serializer, bool async, CancellationToken cancellationToken = default)
{
Argument.AssertNotNull(serializer, nameof(serializer));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -20,12 +19,12 @@ public class EventGridEvent
{
/// <summary> Initializes a new instance of <see cref="EventGridEvent"/>. </summary>
/// <param name="subject"> A resource path relative to the topic path. </param>
/// <param name="data"> Event data specific to the event type. </param>
/// <param name="eventType"> The type of the event that occurred. For example, "Contoso.Items.ItemReceived". </param>
/// <param name="dataVersion"> The schema version of the data object. </param>
/// <param name="data"> Event data specific to the event type. </param>
/// <param name="dataSerializationType">The type to use when serializing the data.
/// If not specified, <see cref="object.GetType()"/> will be used on <paramref name="data"/>.</param>
public EventGridEvent(object data, string subject, string eventType, string dataVersion, Type dataSerializationType = default)
public EventGridEvent(string subject, string eventType, string dataVersion, object data, Type dataSerializationType = default)
{
Argument.AssertNotNull(subject, nameof(subject));
Argument.AssertNotNull(data, nameof(data));
Expand Down Expand Up @@ -134,51 +133,42 @@ public static EventGridEvent[] Parse(string requestContent)
/// Deserializes the event payload into a specified event type using the provided <see cref="ObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="serializer"> Custom serializer used to deserialize the payload. </param>
/// <param name="serializer"> A custom serializer used to deserialize the payload. If not provided, the
/// <see cref="JsonObjectSerializer"/> will be used.</param>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from EventGridEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public async Task<T> GetDataAsync<T>(ObjectSerializer serializer, CancellationToken cancellationToken = default)
public async Task<T> GetDataAsync<T>(ObjectSerializer serializer = default, CancellationToken cancellationToken = default)
{
if (Data != null)
if (Data != null && serializer != null)
{
throw new InvalidOperationException("Cannot pass in a custom deserializer if event was not created from EventGridEvent.Parse(), " +
"as event data should already be deserialized and the custom deserializer will not be used.");
}
return await GetDataInternal<T>(serializer, true, cancellationToken).ConfigureAwait(false);
return await GetDataInternal<T>(serializer ?? s_jsonSerializer, true, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Deserializes the event payload into a specified event type using the provided <see cref="ObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="serializer"> Custom serializer used to deserialize the payload. </param>
/// <param name="serializer"> A custom serializer used to deserialize the payload. If not provided, the
/// <see cref="JsonObjectSerializer"/> will be used.</param>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from EventGridEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public T GetData<T>(ObjectSerializer serializer, CancellationToken cancellationToken = default)
public T GetData<T>(ObjectSerializer serializer = default, CancellationToken cancellationToken = default)
{
if (Data != null)
if (Data != null && serializer != null)
{
throw new InvalidOperationException("Cannot pass in a custom deserializer if event was not created from EventGridEvent.Parse(), " +
"as event data should already be deserialized and the custom deserializer will not be used.");
}
return GetDataInternal<T>(serializer, false, cancellationToken).EnsureCompleted();
return GetDataInternal<T>(serializer ?? s_jsonSerializer, false, cancellationToken).EnsureCompleted();
}

/// <summary>
/// Deserializes the event payload into a specified event type using the provided <see cref="JsonObjectSerializer"/>.
/// </summary>
/// <typeparam name="T"> Type of event to deserialize to. </typeparam>
/// <param name="cancellationToken"> The cancellation token to use during deserialization. </param>
/// <exception cref="InvalidOperationException"> Event was not created from EventGridEvent.Parse() method. </exception>
/// <exception cref="InvalidCastException"> Event payload cannot be cast to the specified event type. </exception>
/// <returns> Deserialized payload of the event, cast to the specified type. </returns>
public T GetData<T>(CancellationToken cancellationToken = default)
=> GetDataInternal<T>(s_jsonSerializer, false, cancellationToken).EnsureCompleted();

private async Task<T> GetDataInternal<T>(ObjectSerializer serializer, bool async, CancellationToken cancellationToken = default)
{
Argument.AssertNotNull(serializer, nameof(serializer));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,7 @@ public EventGridPublisherClient(Uri endpoint, AzureKeyCredential credential)
{
}

/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The Shared Access Signature credential used to authenticate with the service.This signature
/// can be constructed using <see cref="BuildSharedAccessSignature"/>.</param>
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential)
: this(endpoint, credential, new EventGridPublisherClientOptions())
{
}

/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <summary>Initalizes a new instance of the <see cref="EventGridPublisherClient"/> class.</summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The key credential used to authenticate with the service.</param>
/// <param name="options">The set of options to use for configuring the client.</param>
Expand Down Expand Up @@ -137,9 +128,9 @@ private async Task<Response> SendCloudNativeCloudEventsInternalAsync(ReadOnlyMem
/// </summary>
/// <param name="endpoint">The topic endpoint. For example, "https://TOPIC-NAME.REGION-NAME-1.eventgrid.azure.net/api/events".</param>
/// <param name="credential">The Shared Access Signature credential used to connect to Azure. This signature
/// can be constructed using <see cref="BuildSharedAccessSignature"/>.</param>
/// can be constructed using the <see cref="EventGridSasBuilder"/>.</param>
/// <param name="options">The set of options to use for configuring the client.</param>
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential, EventGridPublisherClientOptions options)
public EventGridPublisherClient(Uri endpoint, AzureSasCredential credential, EventGridPublisherClientOptions options = default)
{
Argument.AssertNotNull(credential, nameof(credential));
options ??= new EventGridPublisherClientOptions();
Expand Down Expand Up @@ -372,38 +363,6 @@ private async Task<Response> PublishCustomEventsInternal(IEnumerable<object> eve
}
}

/// <summary>
/// Creates a SAS token for use with the Event Grid service.
/// </summary>
/// <param name="endpoint">The path for the event grid topic to which you're sending events. For example, "https://TOPIC-NAME.REGION-NAME.eventgrid.azure.net/eventGrid/api/events".</param>
/// <param name="expirationUtc">Time at which the SAS token becomes invalid for authentication.</param>
/// <param name="key">The key credential used to generate the token.</param>
/// <param name="apiVersion">The service version to use when handling requests made with the SAS token.</param>
/// <returns>The generated SAS token string.</returns>
public static string BuildSharedAccessSignature(Uri endpoint, DateTimeOffset expirationUtc, AzureKeyCredential key, EventGridPublisherClientOptions.ServiceVersion apiVersion = EventGridPublisherClientOptions.LatestVersion)
{
const char Resource = 'r';
const char Expiration = 'e';
const char Signature = 's';

var uriBuilder = new RequestUriBuilder();
uriBuilder.Reset(endpoint);
uriBuilder.AppendQuery("api-version", apiVersion.GetVersionString(), true);
string encodedResource = HttpUtility.UrlEncode(uriBuilder.ToString());
var culture = CultureInfo.CreateSpecificCulture("en-US");
var encodedExpirationUtc = HttpUtility.UrlEncode(expirationUtc.ToString(culture));

string unsignedSas = $"{Resource}={encodedResource}&{Expiration}={encodedExpirationUtc}";
using (var hmac = new HMACSHA256(Convert.FromBase64String(key.Key)))
{
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(unsignedSas)));
string encodedSignature = HttpUtility.UrlEncode(signature);
string signedSas = $"{unsignedSas}&{Signature}={encodedSignature}";

return signedSas;
}
}

private JsonDocument SerializeObjectToJsonDocument(object data, Type type, CancellationToken cancellationToken)
{
using (MemoryStream stream = new MemoryStream())
Expand Down
Loading

0 comments on commit 2a0bbe9

Please sign in to comment.