Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure Event Grid API implementation #848

Merged
merged 10 commits into from
Jul 26, 2023
14 changes: 14 additions & 0 deletions dotnet/DotNetStandardClasses.sln
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GxOffice", "src\dotnetcore\
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "excel", "excel", "{B521FF6D-E081-4DE6-AC00-A9BE3B6439D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GXEventRouter", "src\dotnetcore\Providers\Messaging\GXEventRouter\GXEventRouter.csproj", "{5BBC75F0-E51A-4EBD-A628-92498D319B1D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GXAzureEventGrid", "src\dotnetcore\Providers\Messaging\GXAzureEventGrid\GXAzureEventGrid.csproj", "{7250CDB1-95C4-4822-B01B-3CBD73324CC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -585,6 +589,14 @@ Global
{E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82}.Release|Any CPU.Build.0 = Release|Any CPU
{5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5BBC75F0-E51A-4EBD-A628-92498D319B1D}.Release|Any CPU.Build.0 = Release|Any CPU
{7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7250CDB1-95C4-4822-B01B-3CBD73324CC9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -701,6 +713,8 @@ Global
{BE108BE8-805D-4FCF-86BA-B2EAF581FFB2} = {F900A4AD-7249-41B4-B918-CB9E8C73747C}
{E1CD114A-F8A5-4246-B5E3-FD27EDEC7C82} = {B521FF6D-E081-4DE6-AC00-A9BE3B6439D1}
{B521FF6D-E081-4DE6-AC00-A9BE3B6439D1} = {2261B65E-3757-4E5B-9DCD-EAE8D1E236A3}
{5BBC75F0-E51A-4EBD-A628-92498D319B1D} = {4C43F2DA-59E5-46F5-B691-195449498555}
{7250CDB1-95C4-4822-B01B-3CBD73324CC9} = {30159B0F-BE61-4DB7-AC02-02851426BE4B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E18684C9-7D76-45CD-BF24-E3944B7F174C}
Expand Down
1 change: 1 addition & 0 deletions dotnet/src/dotnetcore/GxClasses/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[assembly: InternalsVisibleTo("AzureFunctionsTest")]
[assembly: InternalsVisibleTo("GXQueue")]
[assembly: InternalsVisibleTo("GXMessageBroker")]
[assembly: InternalsVisibleTo("GXEventRouter")]
[assembly: InternalsVisibleTo("DotNetCoreUnitTest")]
[assembly: InternalsVisibleTo("DotNetCoreWebUnitTest")]
[assembly: InternalsVisibleTo("GeneXus.Deploy.AzureFunctions.Handlers")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Threading.Tasks;
using Azure;
using Azure.Messaging;
using Azure.Messaging.EventGrid;
using GeneXus.Messaging.Common;
using GeneXus.Services;
using GeneXus.Utils;

namespace GeneXus.Messaging.GXAzureEventGrid
{
public class AzureEventGrid : EventRouterBase, IEventRouter
{
public static string Name = "AZUREEVENTGRID";
private EventGridPublisherClient _client;
private string _endpoint;
private string _accessKey;
public AzureEventGrid() : this(null)
{
}
public AzureEventGrid(GXService providerService) : base(providerService)
{
Initialize(providerService);
}
private void Initialize(GXService providerService)
{
ServiceSettings serviceSettings = new(PropertyConstants.EVENT_ROUTER, Name, providerService);
_endpoint = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.URI_ENDPOINT);
_accessKey = serviceSettings.GetEncryptedPropertyValue(PropertyConstants.ACCESS_KEY);
ggallotti marked this conversation as resolved.
Show resolved Hide resolved

//Package Azure.Identity cannot be referenced because it conflicts with Microsoft.Identity.Client pack version
//used transitively by gxmail and dynamoDB projects.

/*if (string.IsNullOrEmpty(_accessKey))

//Try using Active Directory authentication
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove this commented code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ready.

_client = new EventGridPublisherClient(
new Uri(_endpoint),
new DefaultAzureCredential());*/


if (!string.IsNullOrEmpty(_endpoint)) {

_client = new EventGridPublisherClient(
new Uri(_endpoint),
new AzureKeyCredential(_accessKey));
}
else
throw new Exception("Endpoint URI must be set.");
}
public override string GetName()
{
return Name;
}

public bool SendEvent(GXCloudEvent gxCloudEvent, bool binaryData)
{
CloudEvent evt = ToCloudEvent(gxCloudEvent, binaryData);
bool success = false;
try
{
Task<bool> task;
if (_client != null)
{
task = Task.Run<bool>(async () => await sendEvtAsync(evt).ConfigureAwait(false));
success = task.Result;
}
else
{
throw new Exception("There was an error at the Event Grid initialization.");
}
}
catch (AggregateException ae)
{
throw ae;
}
return success;
}
public bool SendEvents(IList<GXCloudEvent> gxCloudEvents, bool binaryData)
{
List<CloudEvent> evts = new List<CloudEvent>();
foreach (GXCloudEvent e in gxCloudEvents)
evts.Add(ToCloudEvent(e, binaryData));

bool success = false;
try
{
Task<bool> task;
if (_client != null)
{
task = Task.Run<bool>(async () => await sendEvtsAsync(evts).ConfigureAwait(false));
success = task.Result;
}
else
{
throw new Exception("There was an error at the Event Grid initialization.");
}
}
catch (AggregateException ae)
{
throw ae;
}
return success;
}
public bool SendCustomEvents(string jsonString, bool isBinary)
{
if (string.IsNullOrEmpty(jsonString))
{
throw new Exception("Events cannot be empty.");
}
try
{
List<GXEventGridSchema> evts = JsonSerializer.Deserialize<List<GXEventGridSchema>>(jsonString);

IList<EventGridEvent> eventGridEvents = new List<EventGridEvent>();
foreach (GXEventGridSchema e in evts)
eventGridEvents.Add(ToEventGridSchema(e,isBinary));

bool success = false;
try
{
Task<bool> task;
if (_client != null)
{
task = Task.Run<bool>(async () => await sendEventGridSchemaEventsAsync(eventGridEvents).ConfigureAwait(false));
success = task.Result;
}
else
{
throw new Exception("There was an error at the Event Grid initialization.");
}
}
catch (AggregateException ae)
{
throw ae;
}
return success;
}
catch (JsonException)
{
try
{
GXEventGridSchema evt = JsonSerializer.Deserialize<GXEventGridSchema>(jsonString);
bool success = false;
try
{
Task<bool> task;
if (_client != null)
{
task = Task.Run<bool>(async () => await sendEventGridSchemaEventAsync(ToEventGridSchema(evt, isBinary)).ConfigureAwait(false));
success = task.Result;
}
else
{
throw new Exception("There was an error at the Event Grid initialization.");
}
}
catch (AggregateException ae)
{
throw ae;
}
return success;
}
catch (JsonException)
{
throw new Exception("jsonEvents parameter format is no correct. Valid format is AzureEventGrid.EventGridSchema SDT.");
}
}
}

#region Async methods
/// <summary>
/// Send asynchronously an event formatted as Azure EventGrid Schema.
/// </summary>
/// <param name="evt"></param>
/// <returns></returns>
private async Task<bool> sendEventGridSchemaEventAsync(EventGridEvent evt)
{
try
{
await _client.SendEventAsync(evt).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
throw ex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try-catch block is not really needed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All try catch blocks removed. The test is automated in fullgx.

}
}
/// <summary>
/// Send asynchronously a list of events formatted as Azure EventGrid Schema.
/// </summary>
/// <param name="evts"></param>
/// <returns></returns>
private async Task<bool> sendEventGridSchemaEventsAsync(IEnumerable<EventGridEvent> evts)
{
try
{
await _client.SendEventsAsync(evts).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
throw ex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try-catch block is not really needed here.

}
}
/// <summary>
/// Send asynchronously an event formatted as CloudEvent Schema.
/// </summary>
/// <param name="cloudEvent"></param>
/// <returns></returns>
private async Task<bool> sendEvtAsync(CloudEvent cloudEvent)
{
try
{
await _client.SendEventAsync(cloudEvent).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
throw ex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try-catch block is not really needed here.

}
}
/// <summary>
/// Send asynchronously a list of CloudEvent Schema formatted events.
/// </summary>
/// <param name="cloudEvents"></param>
/// <returns></returns>
private async Task<bool> sendEvtsAsync(IEnumerable<CloudEvent> cloudEvents)
{
try
{
await _client.SendEventsAsync(cloudEvents).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
throw ex;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try-catch block is not really needed here.

}
}

#endregion

#region TransformMethods
public bool GetMessageFromException(Exception ex, SdtMessages_Message msg)
{
try
{
RequestFailedException az_ex = (RequestFailedException)ex;
msg.gxTpr_Id = az_ex.ErrorCode.ToString();
msg.gxTpr_Description = az_ex.Message;
return true;
}
catch (Exception)
{
return false;
}
}
private EventGridEvent ToEventGridSchema(GXEventGridSchema gxEventGridSchema, bool isBinary)
{
EventGridEvent evt;
if (isBinary && !string.IsNullOrEmpty(gxEventGridSchema.data))
{
BinaryData binaryData = new BinaryData(gxEventGridSchema.data);
evt = new EventGridEvent(gxEventGridSchema.subject, gxEventGridSchema.eventtype, gxEventGridSchema.dataversion, binaryData);}
else
evt = new EventGridEvent(gxEventGridSchema.subject, gxEventGridSchema.eventtype, gxEventGridSchema.dataversion, gxEventGridSchema.data, null);
if (!string.IsNullOrEmpty(gxEventGridSchema.id))
evt.Id = gxEventGridSchema.id;
if (!string.IsNullOrEmpty(gxEventGridSchema.topic))
evt.Topic = gxEventGridSchema.topic;
if (gxEventGridSchema.eventtime != DateTime.MinValue)
evt.EventTime = gxEventGridSchema.eventtime;

return evt;
}
private CloudEvent ToCloudEvent(GXCloudEvent gxCloudEvent, bool isBinaryData)
{
CloudEvent evt;
Dictionary<string, object> emptyData = new Dictionary<string, object>();
if (string.IsNullOrEmpty(gxCloudEvent.data))
evt = new CloudEvent(source:gxCloudEvent.source, type:gxCloudEvent.type, emptyData);
else
{
if (!isBinaryData)
{
if (string.IsNullOrEmpty(gxCloudEvent.datacontenttype))
gxCloudEvent.datacontenttype = "application/json";
evt = new CloudEvent(gxCloudEvent.source, gxCloudEvent.type, BinaryData.FromString(gxCloudEvent.data),gxCloudEvent.datacontenttype,CloudEventDataFormat.Json);
}
else
{
if (string.IsNullOrEmpty(gxCloudEvent.datacontenttype))
gxCloudEvent.datacontenttype = "application/octet-stream";
evt = new CloudEvent(gxCloudEvent.source, gxCloudEvent.type, BinaryData.FromString(gxCloudEvent.data), gxCloudEvent.datacontenttype,CloudEventDataFormat.Binary);
}
}
if (!string.IsNullOrEmpty(gxCloudEvent.id))
evt.Id = gxCloudEvent.id;
if (!string.IsNullOrEmpty(gxCloudEvent.dataschema))
evt.DataSchema = gxCloudEvent.dataschema;
if (!string.IsNullOrEmpty(gxCloudEvent.subject))
evt.Subject = gxCloudEvent.subject;
if (gxCloudEvent.time != DateTime.MinValue)
evt.Time = gxCloudEvent.time;
return evt;
}
#endregion
}

[DataContract]
public class GXEventGridSchema
{
[DataMember]
public string topic { get; set; }
[DataMember]
public string eventtype { get; set; }
[DataMember]
public string id { get; set; }
[DataMember]
public string subject { get; set; }
[DataMember]
public string data { get; set; }
[DataMember]
public string dataversion { get; set; }

[DataMember]
public DateTime eventtime { get; set; }

[DataMember]
public string metadataversion { get; set; }


}
}
Loading