Skip to content

Commit beac028

Browse files
committed
Add CosmosDB, EventGrid, Service Bus triggered functions.
1 parent 7f88c2b commit beac028

18 files changed

+521
-118
lines changed

gxazureserverless/pom.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,36 @@
4949
<version>1.35.0</version>
5050
</dependency>
5151

52+
<dependency>
53+
<groupId>io.cloudevents</groupId>
54+
<artifactId>cloudevents-api</artifactId>
55+
<version>2.5.0</version>
56+
</dependency>
57+
58+
<dependency>
59+
<groupId>io.cloudevents</groupId>
60+
<artifactId>cloudevents-core</artifactId>
61+
<version>2.3.0</version>
62+
</dependency>
63+
64+
<dependency>
65+
<groupId>io.cloudevents</groupId>
66+
<artifactId>cloudevents-json-jackson</artifactId>
67+
<version>4.0.1</version>
68+
</dependency>
69+
70+
<dependency>
71+
<groupId>com.azure</groupId>
72+
<artifactId>azure-messaging-servicebus</artifactId>
73+
<version>7.17.1</version>
74+
</dependency>
75+
76+
<dependency>
77+
<groupId>com.azure</groupId>
78+
<artifactId>azure-messaging-eventgrid</artifactId>
79+
<version>4.22.3</version>
80+
</dependency>
81+
5282
<dependency>
5383
<groupId>com.fasterxml.jackson.core</groupId>
5484
<artifactId>jackson-databind</artifactId>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.genexus.cloud.serverless.azure.handler;
2+
import com.genexus.cloud.serverless.model.*;
3+
4+
import com.microsoft.azure.functions.annotation.*;
5+
import com.microsoft.azure.functions.ExecutionContext;
6+
7+
import java.time.Instant;
8+
import java.util.List;
9+
import java.util.*;
10+
11+
public class AzureBlobStorageHandler extends AzureEventHandler{
12+
public AzureBlobStorageHandler() throws Exception {
13+
super();
14+
}
15+
public void run(
16+
@BlobTrigger(name = "content", path = "%blob_path%", dataType = "binary") byte[] content,
17+
@BindingName("name") String name,
18+
final ExecutionContext context
19+
) throws Exception {
20+
context.getLogger().info("GeneXus Blob Storage trigger handler. Function processed: " + context.getFunctionName() + " Invocation Id: " + context.getInvocationId());
21+
22+
EventMessages msgs = new EventMessages();
23+
EventMessage msg = new EventMessage();
24+
msg.setMessageId(context.getInvocationId());
25+
msg.setMessageSourceType(EventMessageSourceType.BLOB);
26+
27+
Instant nowUtc = Instant.now();
28+
msg.setMessageDate(Date.from(nowUtc));
29+
msg.setMessageData(Base64.getEncoder().encodeToString(content));
30+
31+
List<EventMessageProperty> msgAtts = msg.getMessageProperties();
32+
33+
msgAtts.add(new EventMessageProperty("Id", context.getInvocationId()));
34+
msgAtts.add(new EventMessageProperty("name", name));
35+
36+
msgs.add(msg);
37+
38+
SetupServerlessMappings(context.getFunctionName());
39+
40+
try {
41+
EventMessageResponse response = dispatchEvent(msgs, Base64.getEncoder().encodeToString(content));
42+
if (response.hasFailed()) {
43+
logger.error(String.format("Messages were not handled. Error: %s", response.getErrorMessage()));
44+
throw new RuntimeException(response.getErrorMessage()); //Throw the exception so the runtime can Retry the operation.
45+
}
46+
} catch (Exception e) {
47+
logger.error("HandleRequest execution error", e);
48+
throw e; //Throw the exception so the runtime can Retry the operation.
49+
}
50+
51+
}
52+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.genexus.cloud.serverless.azure.handler;
2+
import com.genexus.cloud.serverless.Helper;
3+
import com.genexus.cloud.serverless.model.*;
4+
import com.microsoft.azure.functions.ExecutionContext;
5+
import com.microsoft.azure.functions.annotation.CosmosDBTrigger;
6+
import com.sun.jna.platform.win32.Guid;
7+
8+
import java.util.Date;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
public class AzureCosmosDBHandler extends AzureEventHandler{
13+
14+
public AzureCosmosDBHandler() throws Exception {
15+
super();
16+
}
17+
public void run(
18+
@CosmosDBTrigger(name = "itemIn", databaseName = "%CosmosDB_Database_Name%", containerName = "%Container_Name%", leaseContainerName = "%lease_Container_Name%", connection = "%CosmosDB_Connection%") List<Map<String,Object>> items ,
19+
final ExecutionContext context) throws Exception {
20+
21+
context.getLogger().info("GeneXus CosmosDB trigger handler. Function processed: " + context.getFunctionName() + " Invocation Id: " + context.getInvocationId());
22+
23+
Guid.GUID eventId = new Guid.GUID(context.getInvocationId());
24+
25+
EventMessages msgs = new EventMessages();
26+
EventMessagesList eventMessagesList = new EventMessagesList();
27+
28+
switch (executor.methodSignatureIdx) {
29+
case 0:
30+
msgs = setupEventMessages(eventId,items);
31+
break;
32+
case 4:
33+
eventMessagesList = setupEventMessagesList(items);
34+
break;
35+
default:
36+
break;
37+
}
38+
SetupServerlessMappings(context.getFunctionName());
39+
40+
try {
41+
EventMessageResponse response = dispatchEvent(msgs, eventMessagesList, Helper.toJSONString(items));
42+
if (response.hasFailed()) {
43+
logger.error(String.format("Messages were not handled. Error: %s", response.getErrorMessage()));
44+
throw new RuntimeException(response.getErrorMessage()); //Throw the exception so the runtime can Retry the operation.
45+
}
46+
47+
} catch (Exception e) {
48+
logger.error("HandleRequest execution error", e);
49+
throw e; //Throw the exception so the runtime can Retry the operation.
50+
}
51+
52+
}
53+
private EventMessagesList setupEventMessagesList(List<Map<String,Object>> jsonList)
54+
{
55+
EventMessagesList messagesList = new EventMessagesList();
56+
for (Map<String, Object> json : jsonList) {
57+
messagesList.addItem(Helper.toJSONString(json));
58+
}
59+
return messagesList;
60+
}
61+
private EventMessages setupEventMessages(Guid.GUID eventId, List<Map<String,Object>> jsonList)
62+
{
63+
EventMessages msgs = new EventMessages();
64+
65+
for (Map<String, Object> json : jsonList) {
66+
67+
String idValue = "";
68+
EventMessage msg = new EventMessage();
69+
msg.setMessageDate(new Date());
70+
msg.setMessageSourceType(EventMessageSourceType.COSMOSDB);
71+
72+
List<EventMessageProperty> msgAtts = msg.getMessageProperties();
73+
74+
for (Map.Entry<String, Object> entry : json.entrySet()) {
75+
String key = entry.getKey();
76+
String value = entry.getValue().toString();
77+
78+
msgAtts.add(new EventMessageProperty(key, value));
79+
80+
if (key.equals("id"))
81+
idValue = value;
82+
}
83+
String messageId = eventId.toString() + "_" + idValue;
84+
msg.setMessageId(messageId);
85+
msgs.add(msg);
86+
}
87+
return msgs;
88+
}
89+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.genexus.cloud.serverless.azure.handler;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import com.genexus.cloud.serverless.model.EventMessage;
5+
import com.genexus.cloud.serverless.model.EventMessageProperty;
6+
import com.genexus.cloud.serverless.model.EventMessageResponse;
7+
import com.genexus.cloud.serverless.model.EventMessages;
8+
import com.microsoft.azure.functions.ExecutionContext;
9+
import com.microsoft.azure.functions.annotation.EventGridTrigger;
10+
import io.cloudevents.CloudEvent;
11+
12+
import java.util.Date;
13+
import java.util.List;
14+
import java.util.Objects;
15+
16+
public class AzureEventGridCloudHandler extends AzureEventHandler{
17+
18+
public AzureEventGridCloudHandler() throws Exception {super();}
19+
20+
public void run(
21+
@EventGridTrigger(name = "eventgridEvent") String eventJson,
22+
final ExecutionContext context) throws Exception {
23+
24+
context.getLogger().info("GeneXus Event Grid CloudEvents trigger handler. Function processed: " + context.getFunctionName() + " Invocation Id: " + context.getInvocationId());
25+
SetupServerlessMappings(context.getFunctionName());
26+
27+
try {
28+
ObjectMapper objectMapper = new ObjectMapper();
29+
objectMapper.registerModule(io.cloudevents.jackson.JsonFormat.getCloudEventJacksonModule());
30+
CloudEvent cloudEvent = objectMapper.readValue(eventJson, CloudEvent.class);
31+
32+
EventMessages msgs = new EventMessages();
33+
EventMessage msg = new EventMessage();
34+
msg.setMessageId(cloudEvent.getId());
35+
msg.setMessageSourceType(cloudEvent.getType());
36+
msg.setMessageVersion("");
37+
38+
msg.setMessageDate(new Date());
39+
msg.setMessageData(Objects.requireNonNull(cloudEvent.getData()).toString());
40+
41+
List<EventMessageProperty> msgAtts = msg.getMessageProperties();
42+
43+
msgAtts.add(new EventMessageProperty("Id", cloudEvent.getId()));
44+
msgAtts.add(new EventMessageProperty("Subject",cloudEvent.getSubject()));
45+
msgAtts.add(new EventMessageProperty("DataContentType",cloudEvent.getDataContentType()));
46+
msgAtts.add(new EventMessageProperty("DataSchema", Objects.requireNonNull(cloudEvent.getDataSchema()).toString()));
47+
msgAtts.add(new EventMessageProperty("Source", cloudEvent.getSource().toString()));
48+
msgAtts.add(new EventMessageProperty("SpecVersion", cloudEvent.getSpecVersion().toString()));
49+
msgAtts.add(new EventMessageProperty("Time", Objects.requireNonNull(cloudEvent.getTime()).toString()));
50+
51+
msgs.add(msg);
52+
53+
EventMessageResponse response = dispatchEvent(msgs, eventJson);
54+
if (response.hasFailed()) {
55+
logger.error(String.format("Messages were not handled. Error: %s", response.getErrorMessage()));
56+
throw new RuntimeException(response.getErrorMessage()); //Throw the exception so the runtime can Retry the operation.
57+
}
58+
} catch (Exception e) {
59+
logger.error("HandleRequest execution error", e);
60+
throw e; //Throw the exception so the runtime can Retry the operation.
61+
}
62+
}
63+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.genexus.cloud.serverless.azure.handler;
2+
3+
import com.azure.messaging.eventgrid.EventGridEvent;
4+
import com.genexus.cloud.serverless.Helper;
5+
import com.genexus.cloud.serverless.model.*;
6+
import com.microsoft.azure.functions.ExecutionContext;
7+
import com.microsoft.azure.functions.annotation.EventGridTrigger;
8+
9+
import java.util.Date;
10+
import java.util.List;
11+
12+
public class AzureEventGridHandler extends AzureEventHandler {
13+
public AzureEventGridHandler() throws Exception {
14+
}
15+
16+
public void run(
17+
@EventGridTrigger(name = "eventgridEvent") EventGridEvent event,
18+
final ExecutionContext context) throws Exception {
19+
20+
context.getLogger().info("GeneXus Event Grid trigger handler. Function processed: " + context.getFunctionName() + " Invocation Id: " + context.getInvocationId());
21+
22+
EventMessages msgs = new EventMessages();
23+
EventMessage msg = new EventMessage();
24+
msg.setMessageId(event.getId());
25+
msg.setMessageSourceType(event.getEventType());
26+
msg.setMessageVersion(event.getDataVersion());
27+
28+
msg.setMessageDate(new Date());
29+
msg.setMessageData(event.getData().toString());
30+
31+
List<EventMessageProperty> msgAtts = msg.getMessageProperties();
32+
33+
msgAtts.add(new EventMessageProperty("Id", event.getId()));
34+
35+
msgAtts.add(new EventMessageProperty("Subject",event.getSubject()));
36+
msgAtts.add(new EventMessageProperty("Topic",event.getTopic()));
37+
msgAtts.add(new EventMessageProperty("EventTime",event.getEventTime().toString()));
38+
39+
msgs.add(msg);
40+
41+
SetupServerlessMappings(context.getFunctionName());
42+
43+
try {
44+
EventMessageResponse response = dispatchEvent(msgs, Helper.toJSONString(event));
45+
if (response.hasFailed()) {
46+
logger.error(String.format("Messages were not handled. Error: %s", response.getErrorMessage()));
47+
throw new RuntimeException(response.getErrorMessage()); //Throw the exception so the runtime can Retry the operation.
48+
}
49+
50+
} catch (Exception e) {
51+
logger.error("HandleRequest execution error", e);
52+
throw e; //Throw the exception so the runtime can Retry the operation.
53+
}
54+
55+
}
56+
}

gxazureserverless/src/main/java/com/genexus/cloud/serverless/azure/handler/AzureEventHandler.java

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,40 @@
11
package com.genexus.cloud.serverless.azure.handler;
22

3-
import com.genexus.cloud.serverless.exception.FunctionConfigurationException;
43
import com.genexus.cloud.serverless.GXProcedureExecutor;
54
import com.genexus.cloud.serverless.ServerlessBaseEventHandler;
5+
import com.genexus.cloud.serverless.exception.FunctionConfigurationException;
66
import com.genexus.cloud.serverless.helpers.GlobalConfigurationCache;
77

8-
import java.util.List;
9-
108
public class AzureEventHandler extends ServerlessBaseEventHandler<AzureFunctionConfiguration> {
119
public AzureEventHandler() throws Exception {
1210
super();
1311
}
14-
protected void SetupServerlessMappings(String functionName) throws Exception {
12+
protected void SetupServerlessMappings(String functionName) throws FunctionConfigurationException, ClassNotFoundException {
1513
logger.debug("Initializing Function configuration");
1614

1715
//First use Environment variable, then try reading from the gxazmappings.json file
18-
19-
String envvar = String.format("GX_AZURE_%s_CLASS",functionName.trim().toUpperCase());
20-
if (System.getenv(envvar) != null) {
21-
String gxObjectClassName = System.getenv(envvar);
22-
entryPointClass = Class.forName(gxObjectClassName);
23-
functionConfiguration = new AzureFunctionConfiguration(functionName,gxObjectClassName);
24-
}
25-
if (entryPointClass == null)
26-
{
27-
try {
28-
functionConfiguration = GlobalConfigurationCache.getInstance().getAzureFunctionConfiguration(functionName);
29-
entryPointClass = Class.forName(functionConfiguration.getGXClassName());
16+
String envvar = String.format("GX_AZURE_%s_CLASS", functionName.trim().toUpperCase());
17+
if (entryPointClass == null) {
18+
if (System.getenv(envvar) != null) {
19+
String gxObjectClassName = System.getenv(envvar);
20+
entryPointClass = Class.forName(gxObjectClassName);
21+
functionConfiguration = new AzureFunctionConfiguration(functionName, gxObjectClassName);
3022
}
31-
catch (Exception e) {
32-
logger.error(String.format("Failed to initialize Application configuration for %s",functionName), e);
33-
throw e;
23+
else {
24+
try {
25+
functionConfiguration = GlobalConfigurationCache.getInstance().getAzureFunctionConfiguration(functionName);
26+
entryPointClass = Class.forName(functionConfiguration.getGXClassName());
27+
} catch (Exception e) {
28+
logger.error(String.format("Failed to initialize Application configuration for %s", functionName), e);
29+
throw e;
30+
}
3431
}
3532
}
3633
if (entryPointClass != null)
3734
executor = new GXProcedureExecutor(entryPointClass);
3835
else {
3936
logger.error(String.format("GeneXus Entry point class for function %s was not specified. Set %s Environment Variable.",functionName,envvar));
40-
throw new Exception(String.format("GeneXus Entry point class for function %s was not specified. Set %s Environment Variable.",functionName,envvar));
37+
throw new FunctionConfigurationException(String.format("GeneXus Entry point class for function %s was not specified. Set %s Environment Variable.",functionName,envvar));
4138
}
4239
}
4340
@Override
@@ -56,7 +53,7 @@ protected AzureFunctionConfiguration createFunctionConfiguration() {
5653
return new AzureFunctionConfiguration();
5754
}
5855
@Override
59-
protected void InitializeServerlessConfig() throws Exception {
56+
protected void InitializeServerlessConfig(){
6057
}
6158

6259
}

gxazureserverless/src/main/java/com/genexus/cloud/serverless/azure/handler/AzureFunctionConfiguration.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,15 @@
22

33
import com.fasterxml.jackson.annotation.JsonProperty;
44
import com.genexus.cloud.serverless.ServerlessFunctionConfiguration;
5-
import com.genexus.cloud.serverless.exception.FunctionConfigurationException;
6-
import com.genexus.diagnostics.core.ILogger;
7-
import com.genexus.specific.java.LogManager;
8-
9-
import java.util.List;
10-
import java.util.Optional;
115

126
public class AzureFunctionConfiguration extends ServerlessFunctionConfiguration {
13-
private final static ILogger logger = LogManager.initialize(".", AzureFunctionConfiguration.class);
14-
private final static String FUNCTION_CONFIG_PATH = "gxazmappings.json";
7+
158
@JsonProperty("FunctionName")
169
private String functionName;
1710
@JsonProperty("GXEntrypoint")
1811
private String gxEntrypoint;
1912

20-
public AzureFunctionConfiguration()
21-
{
13+
public AzureFunctionConfiguration() {
2214
}
2315
public AzureFunctionConfiguration(String functionName, String gxEntrypoint)
2416
{

0 commit comments

Comments
 (0)