Skip to content

Commit 7e60ef5

Browse files
authored
Add support to azure functions triggered by Event Grid (#920)
1 parent 49e69a0 commit 7e60ef5

File tree

7 files changed

+524
-3
lines changed

7 files changed

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

0 commit comments

Comments
 (0)