Skip to content

Commit e0b459a

Browse files
committed
Add initial TriggerMesh transformation support
1 parent 702e18b commit e0b459a

File tree

1 file changed

+143
-7
lines changed
  • components/sbm-recipes-mule-to-boot/src/main/java/org/springframework/sbm/mule/actions/javadsl/translators/dwl

1 file changed

+143
-7
lines changed

components/sbm-recipes-mule-to-boot/src/main/java/org/springframework/sbm/mule/actions/javadsl/translators/dwl/DwlTransformTranslator.java

Lines changed: 143 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@
2424
import org.springframework.stereotype.Component;
2525

2626
import javax.xml.namespace.QName;
27-
import java.util.Collections;
2827
import java.util.Map;
2928
import java.util.Set;
3029

3130
@Component
3231
public class DwlTransformTranslator implements MuleComponentToSpringIntegrationDslTranslator<TransformMessageType> {
33-
public static final String STATEMENT_CONTENT = ".transform($CLASSNAME::transform)";
34-
private static final String externalClassContentPrefixTemplate = "package com.example.javadsl;\n" +
35-
"\n" +
32+
public static final String TRANSFORM_STATEMENT_CONTENT = ".transform($CLASSNAME::transform)";
33+
public static final String externalPackageName = "package com.example.javadsl;\n\n";
34+
35+
/* Define the stubs for adding the transformation as a comment to be addressed */
36+
private static final String externalClassContentPrefixTemplate = externalPackageName +
3637
"public class $CLASSNAME {\n" +
3738
" /*\n" +
3839
" * TODO:\n" +
@@ -46,6 +47,87 @@ public class DwlTransformTranslator implements MuleComponentToSpringIntegrationD
4647
" }\n" +
4748
"}";
4849

50+
/* Define the TriggerMesh specific stubs when enabled. This will capture the transformation, and send it along
51+
* with the payload to the TriggerMesh Dataweave Transformation Service.
52+
*/
53+
// Independent class to capture parts of the message header from the spring integration to inject into the transformation
54+
private static final String triggermeshDWPayload = "" +
55+
"package com.example.javadsl;\n\n" +
56+
"import lombok.Data;\n\n" +
57+
"@Data\n" +
58+
"public class TmDwPayload {\n" +
59+
"\tprivate String id;\n" +
60+
"\tprivate String source;\n" +
61+
"\tprivate String sourceType;\n" +
62+
"\tprivate String payload;\n" +
63+
"}\n";
64+
65+
private static final String triggermeshPayloadHandlerContent = "" +
66+
".<LinkedMultiValueMap<String, String>>handle((p, h) -> {\n" +
67+
" TmDwPayload dwPayload = new TmDwPayload();\n" +
68+
" dwPayload.setId(h.getId().toString());\n" +
69+
" dwPayload.setSourceType(h.get(\"contentType\").toString());\n" +
70+
" dwPayload.setSource(h.get(\"http_requestUrl\").toString());\n" +
71+
" dwPayload.setPayload(p.toString());\n" +
72+
" return dwPayload;\n" +
73+
" })";
74+
private static final String triggermeshImportsTemplate = "import com.fasterxml.jackson.databind.ObjectMapper;\n\n" +
75+
"import java.net.URI;\n" +
76+
"import java.net.http.HttpClient;\n" +
77+
"import java.net.http.HttpRequest;\n" +
78+
"import java.net.http.HttpResponse;\n";
79+
80+
private static final String triggermeshDWTransformationClass = "" +
81+
"\tpublic static class DataWeavePayload {\n" +
82+
"\t public String input_data;\n" +
83+
"\t public String spell;\n" +
84+
"\t public String input_content_type;\n" +
85+
"\t public String output_content_type;\n" +
86+
"\t};";
87+
88+
private static final String triggermeshClassTemplate = externalPackageName + triggermeshImportsTemplate +
89+
"\npublic class $CLASSNAME {\n" +
90+
triggermeshDWTransformationClass + "\n\n" +
91+
"\tpublic static String transform(TmDwPayload payload) {\n" +
92+
"\t\tString uuid = payload.getId();\n" +
93+
"\t\tString url = System.getenv(\"K_SINK\");\n" + // NOTE: K_SINK is the URL for the target transformation service
94+
"\t\tHttpClient client = HttpClient.newHttpClient();\n" +
95+
"\t\tHttpRequest.Builder requestBuilder;\n" +
96+
"\t\tDataWeavePayload dwPayload = new DataWeavePayload();\n" +
97+
"\t\tif (payload.getSourceType().contains(\";\")) {\n" +
98+
"\t\t\tdwPayload.input_content_type = payload.getSourceType().split(\";\")[0];\n" +
99+
"\t\t} else {\n" +
100+
"\t\t\tdwPayload.input_content_type = payload.getSourceType();\n" +
101+
"\t\t}\n" +
102+
"\t\tdwPayload.output_content_type = \"$OUTPUT_CONTENT_TYPE\";\n" +
103+
"\t\t//TODO: Verify the spell conforms to Dataweave 2.x: https://docs.mulesoft.com/mule-runtime/4.4/migration-dataweave\n" +
104+
"\t\tdwPayload.spell = \"$DWSPELL\";\n" +
105+
"\t\tdwPayload.input_data = payload.getPayload();\n" +
106+
"\t\tString body;\n\n" +
107+
"\t\ttry {\n" +
108+
"\t\t\trequestBuilder = HttpRequest.newBuilder(new URI(url));\n" +
109+
"\t\t\tObjectMapper om = new ObjectMapper();\n" +
110+
"\t\t\tbody = om.writeValueAsString(dwPayload);\n" +
111+
"\t\t} catch (Exception e) {\n" +
112+
"\t\t\tSystem.out.println(\"Error sending request: \" + e.toString());\n" +
113+
"\t\t\treturn null;\n" +
114+
"\t\t}\n\n" +
115+
"\t\trequestBuilder.setHeader(\"content-type\", \"application/json\");\n" +
116+
"\t\trequestBuilder.setHeader(\"ce-specversion\", \"1.0\");\n" +
117+
"\t\trequestBuilder.setHeader(\"ce-source\", payload.getSource());\n" +
118+
"\t\trequestBuilder.setHeader(\"ce-type\", \"io.triggermesh.dataweave.transform\");\n" +
119+
"\t\trequestBuilder.setHeader(\"ce-id\", payload.getId());\n\n" +
120+
"\t\tHttpRequest request = requestBuilder.POST(HttpRequest.BodyPublishers.ofString(body)).build();\n\n" +
121+
"\t\ttry {\n" +
122+
"\t\t\tHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\n" +
123+
"\t\t\t // TODO: verify the response status and body\n" +
124+
"\t\t\treturn response.body();\n" +
125+
"\t\t} catch (Exception e) {\n" +
126+
"\t\t\tSystem.out.println(\"Error sending event: \" + e.toString());\n" +
127+
"\t\t\treturn null;\n" +
128+
"\t\t}\n" +
129+
"\t}\n";
130+
49131
@Override
50132
public Class<TransformMessageType> getSupportedMuleType() {
51133
return TransformMessageType.class;
@@ -66,7 +148,8 @@ public DslSnippet translate(
66148
return formExternalFileBasedDSLSnippet(component);
67149
}
68150

69-
return formEmbeddedDWLBasedDSLSnippet(component, Helper.sanitizeForBeanMethodName(flowName), id);
151+
//return formEmbeddedDWLBasedDSLSnippet(component, Helper.sanitizeForBeanMethodName(flowName), id);
152+
return formTriggerMeshDWLBasedDSLSnippet(component, Helper.sanitizeForBeanMethodName(flowName), id);
70153
}
71154

72155
return noSupportDslSnippet();
@@ -90,11 +173,52 @@ private DslSnippet formEmbeddedDWLBasedDSLSnippet(TransformMessageType component
90173
replaceClassName(externalClassContentSuffixTemplate, className);
91174

92175
return DslSnippet.builder()
93-
.renderedSnippet(replaceClassName(STATEMENT_CONTENT, className))
176+
.renderedSnippet(replaceClassName(TRANSFORM_STATEMENT_CONTENT, className))
94177
.externalClassContent(externalClassContent)
95178
.build();
96179
}
97180

181+
private DslSnippet formTriggerMeshDWLBasedDSLSnippet(TransformMessageType component, String flowName, int id) {
182+
String className = capitalizeFirstLetter(flowName) + "TransformTM_" + id;
183+
String dwlSpell = component.getSetPayload().getContent().toString();
184+
185+
// Locate the output content type based on the spell. If it isn't present, default to
186+
// application/json
187+
String outputContentType = getSpellOutputType(dwlSpell);
188+
String tmTransformationContent = triggermeshClassTemplate
189+
.replace("$CLASSNAME", className)
190+
.replace("$OUTPUT_CONTENT_TYPE", outputContentType)
191+
.replace("$DWSPELL", sanitizeSpell(dwlSpell));
192+
193+
// Build the dw payload
194+
return DslSnippet.builder()
195+
.externalClassContent(triggermeshDWPayload)
196+
.renderedSnippet(triggermeshPayloadHandlerContent + "\n" + replaceClassName(TRANSFORM_STATEMENT_CONTENT, className))
197+
.externalClassContent(tmTransformationContent)
198+
.requiredImports(Set.of("java.net.URI", "java.net.http.HttpClient", "java.net.http.HttpRequest", "java.net.http.HttpResponse", "com.fasterxml.jackson.databind.ObjectMapper"))
199+
.build();
200+
}
201+
202+
private String getSpellOutputType(String spell) {
203+
String spellOutputType = "application/json";
204+
205+
String []spellElements = spell.split(" ");
206+
for (int i = 0; i < spellElements.length; i++) {
207+
if (spellElements[i].equals("%output")) {
208+
spellOutputType = spellElements[i+1].trim();
209+
break;
210+
} else if (spellElements[i].equals("---")) {
211+
break;
212+
}
213+
}
214+
215+
if (spellOutputType.contains(";")) {
216+
spellOutputType = spellOutputType.split(";")[0];
217+
}
218+
219+
return spellOutputType;
220+
}
221+
98222
private DslSnippet formExternalFileBasedDSLSnippet(TransformMessageType component) {
99223
String resource = component.getSetPayload().getResource();
100224
String className = sanitizeForClassName(resource);
@@ -104,7 +228,7 @@ private DslSnippet formExternalFileBasedDSLSnippet(TransformMessageType componen
104228
+ resource.replace("classpath:", "")
105229
+ replaceClassName(externalClassContentSuffixTemplate, className);
106230
return DslSnippet.builder()
107-
.renderedSnippet(replaceClassName(STATEMENT_CONTENT, className))
231+
.renderedSnippet(replaceClassName(TRANSFORM_STATEMENT_CONTENT, className))
108232
.externalClassContent(content)
109233
.build();
110234
}
@@ -115,6 +239,18 @@ public static String sanitizeForClassName(String classNameCandidate) {
115239
return (capitalizeFirstLetter(sanitizedClassName) + "Transform");
116240
}
117241

242+
// Remove the leading/trailing spaces, [], ensure the double quote marks are escaped, and swap out the newlines
243+
private static String sanitizeSpell(String spell) {
244+
String s = spell.trim();
245+
if (s.charAt(0) == '[' && s.charAt(s.length() -1) == ']') {
246+
s = s.substring(1);
247+
s = s.substring(0, s.length() - 1);
248+
}
249+
s = s.replace("\"", "\\\"");
250+
s = s.replace("\n", "\\n");
251+
return s;
252+
}
253+
118254
private boolean isComponentReferencingAnExternalFile(TransformMessageType component) {
119255
return component.getSetPayload().getContent().isEmpty();
120256
}

0 commit comments

Comments
 (0)