Skip to content

Commit e87829f

Browse files
committed
Add support to azure functions triggered by Event Grid
1 parent 2ab253d commit e87829f

File tree

7 files changed

+592
-3
lines changed

7 files changed

+592
-3
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Azure.Messaging;
2+
using GeneXus.Deploy.AzureFunctions.Handlers.Helpers;
3+
using Microsoft.Azure.Functions.Worker;
4+
5+
namespace EventGridTriggerDummy
6+
{
7+
public static class EventGridFunction
8+
{
9+
[Function("EventGridFunction")]
10+
public static void Run([EventGridTrigger] CustomEventType input, FunctionContext context)
11+
{
12+
13+
}
14+
[Function("EventGridFunctionCloudSchema")]
15+
public static void Run([EventGridTrigger] CloudEvent input, FunctionContext context)
16+
{
17+
18+
}
19+
}
20+
}
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Text;
8+
using System.Text.Json;
9+
using GeneXus.Application;
10+
using GeneXus.Deploy.AzureFunctions.Handlers.Helpers;
11+
using GeneXus.Metadata;
12+
using GeneXus.Utils;
13+
using Microsoft.Azure.Functions.Worker;
14+
using Microsoft.Extensions.Logging;
15+
16+
namespace GeneXus.Deploy.AzureFunctions.EventGridHandler
17+
{
18+
public class EventGridTriggerHandler
19+
{
20+
private ICallMappings _callmappings;
21+
22+
public EventGridTriggerHandler(ICallMappings callMappings)
23+
{
24+
_callmappings = callMappings;
25+
}
26+
public void Run(CustomEventType input, FunctionContext context)
27+
{
28+
var logger = context.GetLogger("EventGridTriggerHandler");
29+
string functionName = context.FunctionDefinition.Name;
30+
Guid eventId = new Guid(context.InvocationId);
31+
logger.LogInformation($"GeneXus Event Grid trigger handler. Function processed: {functionName}. Event Id: {eventId}. Function executed at: {DateTime.Now}.");
32+
33+
try
34+
{
35+
ProcessEvent(context, logger, input, eventId.ToString());
36+
}
37+
catch (Exception ex) //Catch System exception and retry
38+
{
39+
logger.LogError(ex.ToString());
40+
throw;
41+
}
42+
}
43+
private void ProcessEvent(FunctionContext context, ILogger log, CustomEventType input, string eventId)
44+
{
45+
CallMappings callmap = (CallMappings)_callmappings;
46+
47+
GxAzMappings map = callmap.mappings is object ? callmap.mappings.First(m => m.FunctionName == context.FunctionDefinition.Name) : null;
48+
string gxProcedure = (map != null && map is object) ? map.GXEntrypoint : string.Empty;
49+
string exMessage;
50+
51+
if (!string.IsNullOrEmpty(gxProcedure))
52+
{
53+
try
54+
{
55+
StringBuilder sb1 = new StringBuilder(gxProcedure);
56+
sb1.Append(".dll");
57+
string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), sb1.ToString());
58+
Assembly obj = Assembly.LoadFile(path);
59+
60+
StringBuilder sb2 = new StringBuilder("GeneXus.Programs.");
61+
sb2.Append(gxProcedure);
62+
63+
Type objexec = obj.GetType(sb2.ToString());
64+
if (objexec != null)
65+
{
66+
object objgxproc = Activator.CreateInstance(objexec);
67+
var method = objexec.GetMethod("execute");
68+
ParameterInfo[] parameters = method.GetParameters();
69+
70+
//Check parameters
71+
72+
if (parameters.Length != 2)
73+
{
74+
//Thrown to the Azure monitor
75+
76+
exMessage = string.Format("{0} Error for Event Id {1}: the number of parameters in GeneXus procedure is not correct.", FunctionExceptionType.SysRuntimeError, eventId);
77+
throw new Exception(exMessage); //Send to retry if possible.
78+
}
79+
else
80+
{
81+
//Two valid signatures for the GX procedure:
82+
//parm(in:&EventMessageCollection, out:&ExternalEventMessageResponse );
83+
//parm(in:&rawData, out:&ExternalEventMessageResponse );
84+
85+
GxContext gxcontext = new GxContext();
86+
object[] parametersdata;
87+
parametersdata = new object[] { null };
88+
89+
if (parameters[0].ParameterType == typeof(string))
90+
{
91+
string eventMessageSerialized = string.Empty;
92+
eventMessageSerialized = JsonSerializer.Serialize(input);
93+
parametersdata = new object[] { eventMessageSerialized, null };
94+
}
95+
else
96+
{
97+
98+
//Initialization
99+
100+
Type eventMessagesType = parameters[0].ParameterType; //SdtEventMessages
101+
GxUserType eventMessages = (GxUserType)Activator.CreateInstance(eventMessagesType, new object[] { gxcontext }); // instance of SdtEventMessages
102+
103+
IList eventMessage = (IList)ClassLoader.GetPropValue(eventMessages, "gxTpr_Eventmessage");//instance of GXBaseCollection<SdtEventMessage>
104+
Type eventMessageItemType = eventMessage.GetType().GetGenericArguments()[0];//SdtEventMessage
105+
106+
GxUserType eventMessageItem = (GxUserType)Activator.CreateInstance(eventMessageItemType, new object[] { gxcontext }); // instance of SdtEventMessage
107+
108+
IList eventMessageProperties = (IList)ClassLoader.GetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties");//instance of GXBaseCollection<GeneXus.Programs.genexusserverlessapi.SdtEventMessageProperty>
109+
Type eventMessPropsItemType = eventMessageProperties.GetType().GetGenericArguments()[0];//SdtEventMessageProperty
110+
111+
GxUserType eventMessageProperty;
112+
113+
if (input.Subject != null)
114+
{
115+
eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Subject", input.Subject, gxcontext);
116+
eventMessageProperties.Add(eventMessageProperty);
117+
}
118+
119+
if (input.Topic != null)
120+
{
121+
eventMessageProperty = EventMessagePropertyMapping.CreateEventMessageProperty(eventMessPropsItemType, "Topic", input.Topic, gxcontext);
122+
eventMessageProperties.Add(eventMessageProperty);
123+
}
124+
125+
//Event
126+
127+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageid", input.Id);
128+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagesourcetype", input.EventType);
129+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageversion", input.DataVersion);
130+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessageproperties", eventMessageProperties);
131+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedate", input.EventTime);
132+
ClassLoader.SetPropValue(eventMessageItem, "gxTpr_Eventmessagedata", toJsonString(input.Data));
133+
134+
135+
//List of Events
136+
eventMessage.Add(eventMessageItem);
137+
parametersdata = new object[] { eventMessages, null };
138+
}
139+
140+
try
141+
{
142+
method.Invoke(objgxproc, parametersdata);
143+
GxUserType EventMessageResponse = parametersdata[1] as GxUserType;//SdtEventMessageResponse
144+
bool result = (bool)ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Handlefailure");
145+
146+
//Error handling
147+
148+
if (result == true) //Must retry
149+
{
150+
exMessage = string.Format("{0} {1}", FunctionExceptionType.AppError, ClassLoader.GetPropValue(EventMessageResponse, "gxTpr_Errormessage"));
151+
throw new Exception(exMessage);
152+
}
153+
else
154+
{
155+
log.LogInformation("(GX function handler) Function finished execution.");
156+
}
157+
}
158+
catch (Exception)
159+
{
160+
log.LogError("{0} Error invoking the GX procedure for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId);
161+
throw; //Throw the exception so the runtime can Retry the operation.
162+
}
163+
}
164+
}
165+
else
166+
{
167+
exMessage = string.Format("{0} GeneXus procedure could not be executed for Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId);
168+
throw new Exception(exMessage);
169+
}
170+
}
171+
catch (Exception)
172+
{
173+
log.LogError("{0} Error processing Event Id {1}.", FunctionExceptionType.SysRuntimeError, eventId);
174+
throw; //Throw the exception so the runtime can Retry the operation.
175+
}
176+
}
177+
else
178+
{
179+
exMessage = string.Format("{0} GeneXus procedure could not be executed while processing Event Id {1}. Reason: procedure not specified in configuration file.", FunctionExceptionType.SysRuntimeError, eventId);
180+
throw new Exception(exMessage);
181+
}
182+
}
183+
private static string toJsonString(IDictionary<string, JsonElement> keyValues)
184+
{
185+
Dictionary<string, object> jsonDictionary = new Dictionary<string, object>();
186+
foreach (var keyvaluepair in keyValues)
187+
{
188+
jsonDictionary[keyvaluepair.Key] = GetJsonValue(keyvaluepair.Value);
189+
}
190+
191+
return JsonSerializer.Serialize(jsonDictionary);
192+
}
193+
194+
static object GetJsonValue(JsonElement element)
195+
{
196+
switch (element.ValueKind)
197+
{
198+
case JsonValueKind.Object:
199+
Dictionary<string, object> obj = new Dictionary<string, object>();
200+
foreach (var prop in element.EnumerateObject())
201+
{
202+
obj[prop.Name] = GetJsonValue(prop.Value);
203+
}
204+
return obj;
205+
206+
case JsonValueKind.Array:
207+
List<object> list = new List<object>();
208+
foreach (var item in element.EnumerateArray())
209+
{
210+
list.Add(GetJsonValue(item));
211+
}
212+
return list;
213+
214+
case JsonValueKind.String:
215+
return element.GetString();
216+
217+
case JsonValueKind.Number:
218+
return element.GetDecimal();
219+
220+
case JsonValueKind.True:
221+
return true;
222+
223+
case JsonValueKind.False:
224+
return false;
225+
226+
case JsonValueKind.Null:
227+
default:
228+
return null;
229+
}
230+
}
231+
}
232+
}
233+

0 commit comments

Comments
 (0)