Skip to content

Commit

Permalink
updates to groovy scripts for variable topics
Browse files Browse the repository at this point in the history
- updates to composeTopic.groovy
- new dynamically generated topicParameters.groovy
  • Loading branch information
dennis-brinley committed Sep 13, 2024
1 parent c93442b commit a956bdb
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 49 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>ep.asyncapi.tool.sap.is</groupId>
<artifactId>ep-asyncapi-sap-is-converter</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<name>ep-asyncapi-sap-is-converter</name>
<description>ep-asyncapi-sap-is-converter</description>
<properties>
Expand Down Expand Up @@ -67,7 +67,7 @@
<dependency>
<groupId>com.solace.ep.codegen</groupId>
<artifactId>solace-ep-codegen</artifactId>
<version>1.1.0</version>
<version>1.1.1</version>
<!-- <scope>system</scope>
<systemPath>${basedir}/src/libs/solace/solace-ep-mule/solace-ep-muleflow-1.0.6-jar-with-dependencies.jar</systemPath> -->
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public byte[] generateISWorkflowArtefactForAppVersion(final ApiClient apiClient,
//resources/scenarios/integrationflow
sapIFlowConverter.createIntegrationFlowFiles(integrationFlowSubDirectory, mapMuleDoc);
//resources/script/
sapIFlowConverter.createDynamicTopicScriptFiles(scriptSubDirectory);
sapIFlowConverter.createDynamicTopicScriptFiles(scriptSubDirectory, mapMuleDoc);

//create zip file
final File zipFile = createZipFile(mainDirectory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.solace.ep.codegen.internal.model.MapMuleDoc;
import com.solace.ep.codegen.internal.model.MapSubFlowEgress;
import com.solace.ep.codegen.internal.model.SchemaInstance;
import com.solace.ep.codegen.sap.iflow.SapIFlowGenerator;

Expand Down Expand Up @@ -353,14 +354,74 @@ private String generateProjectFileContent(final String asyncAPITitle) {
}
}

public void createDynamicTopicScriptFiles(final File scriptSubDirectory) {
/**
* This method will create script files:
* - composeTopic.groovy (static)
* - topicParameters.groovy -- generated dynamically with topic parameters in code, one script function per event
* @param scriptSubDirectory
* @param mapMuleDoc
*/
public void createDynamicTopicScriptFiles(final File scriptSubDirectory, MapMuleDoc mapMuleDoc) {
try {
final InputStream composeTopicScriptInputStream = SapIFlowConverter.class.getResourceAsStream(SapIflorConverterConstants.RESOURCES_SCRIPT_COMPOSE_TOPIC_FILE_PATH);
// final InputStream extractFieldScriptInputStream = SapIFlowConverter.class.getResourceAsStream(SapIflorConverterConstants.RESOURCES_SCRIPT_EXTRACT_FIELD_FILE_PATH);
File composeTopicScriptFile = new File(scriptSubDirectory, "composeTopic.groovy");
// File extractFieldScriptFile = new File(scriptSubDirectory, "extractField.groovy");
FileUtils.copyInputStreamToFile(composeTopicScriptInputStream, composeTopicScriptFile);
// FileUtils.copyInputStreamToFile(extractFieldScriptInputStream, extractFieldScriptFile);

if (mapMuleDoc.getMapEgressSubFlows().isEmpty()) {
return;
} else {
int count = 0;
for (MapSubFlowEgress pub : mapMuleDoc.getMapEgressSubFlows() ) {
if (pub.getSetVariables().size() > 0) {
count++;
}
}
if (count == 0) {
return;
}
}

File topicParametersScriptFile = new File(scriptSubDirectory, "topicParameters.groovy");
FileUtils.writeStringToFile(topicParametersScriptFile, SapIflorConverterConstants.TOPIC_PARAMETERS_GROOVY_HEADER, "UTF-8", false );
int outputChannel = 0; // , functionCount = 0;
for ( MapSubFlowEgress pub : mapMuleDoc.getMapEgressSubFlows() ) {
if ( pub.getSetVariables().size() == 0 ) {
outputChannel++;
continue;
}
if ( pub.isPublishToQueue() ) {
outputChannel++;
continue;
}
String topicParametersGroovyFx = SapIflorConverterConstants.TOPIC_PARAMETERS_GROOVY_FX;
topicParametersGroovyFx = topicParametersGroovyFx.replace(
SapIflorConverterConstants.TP_TOKEN_EVENT_NAME,
pub.getMessageName() != null ? pub.getMessageName() : "UNKNOWN"
);
topicParametersGroovyFx = topicParametersGroovyFx.replace(
SapIflorConverterConstants.TP_TOKEN_TOPIC_ADDRESS_PATTERN,
pub.getPublishAddress() != null ? pub.getPublishAddress() : "UNKNOWN"
);
topicParametersGroovyFx = topicParametersGroovyFx.replace(
SapIflorConverterConstants.TP_TOKEN_FX_INSTANCE,
Integer.toString(outputChannel++)
);
StringBuilder varsList = new StringBuilder();
StringBuilder jsonPath = new StringBuilder();
StringBuilder setValue = new StringBuilder();
int varIndex = 1;
for( Map.Entry<String, String> var : pub.getSetVariables().entrySet() ) {
final String topicVar = var.getKey();
varsList.append(String.format(SapIflorConverterConstants.TOPIC_PARAMETERS_VAR_LIST_PATTERN, topicVar));
jsonPath.append(String.format(SapIflorConverterConstants.TOPIC_PARAMETERS_JSON_PATH_PATTERN, topicVar, varIndex));
setValue.append(String.format(SapIflorConverterConstants.TOPIC_PARAMETERS_SET_VALUE_PATTERN, topicVar, varIndex));
varIndex++;
}
topicParametersGroovyFx = topicParametersGroovyFx.replace(SapIflorConverterConstants.TP_TOKEN_TOPIC_VARS_LIST, varsList.toString());
topicParametersGroovyFx = topicParametersGroovyFx.replace(SapIflorConverterConstants.TP_TOKEN_VARS_JSON_PATH, jsonPath.toString());
topicParametersGroovyFx = topicParametersGroovyFx.replace(SapIflorConverterConstants.TP_TOKEN_VARS_SET_VALUE, setValue.toString());
FileUtils.writeStringToFile(topicParametersScriptFile, topicParametersGroovyFx, "UTF-8", true);
};
} catch (IOException ioException) {
log.error("Error encountered in SapIFlowConverter.createDynamicTopicScriptFiles", ioException);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,61 @@ public class SapIflorConverterConstants {
RESOURCES_MAPPING_SOURCE_TO_DESTINATIONFORMAT_TEMPLATE = "/static/iflowdocument-template/src/main/resources/mapping/sourceToDestinationFormatMmap.xml",
RESOURCES_SCRIPT_COMPOSE_TOPIC_FILE_PATH = "/static/iflowdocument-template/src/main/resources/script/composeTopic.groovy",
RESOURCES_SCRIPT_EXTRACT_FIELD_FILE_PATH = "/static/iflowdocument-template/src/main/resources/script/extractField.groovy";

public static final String
TOPIC_PARAMETERS_GROOVY_HEADER =
"/**\n" + //
" * topicParameters.groovy\n" + //
" * ----------------------\n" + //
" * EDIT this script to define sources for published topic variables\n" + //
" * - One function is generated per event type published to Event Mesh\n" + //
" * - Define entry for variable source as JSON Path (payload) or Set Value (explicit)\n" + //
" * - The JSON Path will be evaluated first, the Set Value can be used as a default\n" + //
" * if the field specified in JSON Path is not found in the payload\n" + //
" **/\n" + //
"import com.sap.gateway.ip.core.customdev.util.Message;\n" + //
"\n" + //
"TV_JSON_PATH_PROPERTY = \"__topicVarsJsonPath__\";\n" + //
"TV_SET_VALUE_PROPERTY = \"__topicVarsSetValue__\";\n" + //
"\n",
TOPIC_PARAMETERS_GROOVY_FX =
"/**\n" + //
" * Define Topic Variables for Event:\n" + //
" * >>$$__EVENT_NAME__$$<<\n" + //
" *\n" + //
" * Topic Address Pattern:\n" + //
" * >>$$__TOPIC_ADDRESS_PATTERN__$$<<\n" + //
" *\n" + //
" * Topic Variables:\n" + //
">>$$__TOPIC_VARS_LIST__$$<<\n" + //
" **/\n" + //
"def Message defineTopicParams_>>$$__FX_INSTANCE__$$<<(Message message) {\n" + //
"\n" + //
" def topicVarsJsonPath = [:]; // Map of topicVariables -> JSON path locations\n" + //
" def topicVarsSetValue = [:]; // Map of topicVariables -> Explicit values\n" + //
"\n" + //
" // Uncomment property value to set JSON Path location in payload\n" + //
" // DO NOT use $. prefix for JSON Path specification\n" + //
">>$$__TOPIC_VARS_JSON_PATH__$$<<\n" + //
"\n" + //
" // Uncomment property entry below to set a topic value directly\n" + //
">>$$__TOPIC_VARS_SET_VALUE__$$<<\n" + //
"\n" + //
" message.setProperty(TV_JSON_PATH_PROPERTY, topicVarsJsonPath);\n" + //
" message.setProperty(TV_SET_VALUE_PROPERTY, topicVarsSetValue);\n" + //
"\n" + //
" return message;\n" + //
"}\n" + //
"\n",
TOPIC_PARAMETERS_JSON_PATH_PATTERN = " // topicVarsJsonPath.%s = \"payload.field%d\"\n",
TOPIC_PARAMETERS_SET_VALUE_PATTERN = " // topicVarsSetValue.%s = \"VALUE%d\"\n",
TOPIC_PARAMETERS_VAR_LIST_PATTERN = " * - %s\n";

public static String
TP_TOKEN_EVENT_NAME = ">>$$__EVENT_NAME__$$<<",
TP_TOKEN_TOPIC_ADDRESS_PATTERN = ">>$$__TOPIC_ADDRESS_PATTERN__$$<<",
TP_TOKEN_TOPIC_VARS_LIST = ">>$$__TOPIC_VARS_LIST__$$<<\n",
TP_TOKEN_FX_INSTANCE = ">>$$__FX_INSTANCE__$$<<",
TP_TOKEN_VARS_JSON_PATH = ">>$$__TOPIC_VARS_JSON_PATH__$$<<\n",
TP_TOKEN_VARS_SET_VALUE = ">>$$__TOPIC_VARS_SET_VALUE__$$<<\n";
}
Original file line number Diff line number Diff line change
@@ -1,62 +1,78 @@
/*
This script will build a topic string with variable elements by extracting
values from a JSON message body
- If there are no variable elements in the topic string then then the set value will be passed on
- If there is no composedTopic pattern then the script will do nothing
*/
/**
* composeTopic.groovy
* -------------------
* This script will build a topic string with variable elements by extracting
* values from a JSON message body OR by replacing variables with explicit values
* - In general, this script should not require editing
* - This script relies on existing message properties:
* - composedTopic -- Topic pattern with variables enclosed in braces: {var}
* - __topicVarsJsonPath__ -- Map of variable names -> JSON path of value
* - __topicVarsSetValue__ -- Map of variable names -> explicit values
**/
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import groovy.json.JsonSlurper;

def Message processData(Message message) {
final GP_TOPICEXP = 'gpath_topicexp_';
final GP_TOPICVAL = 'gpath_topicval_';
final GP_COMPOSED_PATTERN = "gpath_composedTopicPattern";
final COMPOSED_TOPIC = "composedTopic";

def Message composeTopic(Message message) {
final COMPOSED_TOPIC_PROPERTY = 'composedTopic';
final TV_JSON_PATH_PROPERTY = '__topicVarsJsonPath__';
final TV_SET_VALUE_PROPERTY = '__topicVarsSetValue__';

def properties = message.getProperties();
def topicValProperties = new HashMap<String, String>();


try {
def composedTopicString = properties.get(GP_COMPOSED_PATTERN);
if (composedTopicString == null || composedTopicString.length() == 0) {
// This could be an error, or the topic may be set directly on the publisher
// (bypass composed topic logic)
def composedTopicPattern = properties.get(COMPOSED_TOPIC_PROPERTY);

// No topic pattern, nothing to do
if (!composedTopicPattern || composedTopicPattern.isEmpty()) {
// TODO - Error condition?
return message;
}
if (!composedTopicString.contains("{")) {
// Debug log, static topic detected
message.setProperty(COMPOSED_TOPIC, composedTopicString);
// No variables in topic pattern, nothing to do
if (!composedTopicPattern.contains('{')) {
return message;
}

// Extract dynamic topic values from message body
final js = new groovy.json.JsonSlurper();
final request = js.parseText(message.getBody(java.lang.String) as String);
properties.each { prop, texp ->
if (prop.startsWith(GP_TOPICEXP)) {
def varval = groovy.util.Eval.x(request, 'x.' + texp);
topicValProperties.put(GP_TOPICVAL + prop.replace(GP_TOPICEXP, ""), varval.toString());
} else if (prop.startsWith(GP_TOPICVAL)) {
topicValProperties.put(prop, texp.toString());
def tvJsonPath = properties.get(TV_JSON_PATH_PROPERTY)
def tvSetValue = properties.get(TV_SET_VALUE_PROPERTY)

// Special properties with variable content not present, nothing to do
if (!tvJsonPath && !tvSetValue) {
return message;
} else if (tvJsonPath.size() == 0 && tvSetValue.size() == 0) {
return message;
}

def composedTopicString = composedTopicPattern

// Set topic variables from payload
if (tvJsonPath && tvJsonPath.size() > 0) {
final js = new groovy.json.JsonSlurper()
final request = js.parseText(message.getBody(java.lang.String) as String)
tvJsonPath.each { entry ->
def varName = "{$entry.key}"
def varValue = groovy.util.Eval.x(request, 'x.' + entry.value)
if (varName && varValue) {
composedTopicString = composedTopicString.replace(varName, varValue)
}
}
}

// Add Extracted values to message properties
topicValProperties.each { key, value ->
def varName = "{" + key.replace(GP_TOPICVAL, "") + "}";
def varVal = value;
if (varVal == null || varVal.isEmpty()) {
varVal = "NULL";

// Set topic variables directly
if (tvSetValue) {
tvSetValue.each{ entry ->
def varName = "{$entry.key}"
def varValue = entry.value
if (varName && varValue) {
composedTopicString = composedTopicString.replace(varName, varValue)
}
}
composedTopicString = composedTopicString.replace(varName, varVal);
}

// Set the composed topic to expected property
message.setProperty(COMPOSED_TOPIC, composedTopicString);
message.setProperty(COMPOSED_TOPIC_PROPERTY, composedTopicString);

} catch (Exception ex) {
message.setProperty('gpath_error', ex.getMessage());
message.setProperty('composedTopic_error', ex.getMessage());
}
return message;
}

0 comments on commit a956bdb

Please sign in to comment.