24
24
import org .springframework .stereotype .Component ;
25
25
26
26
import javax .xml .namespace .QName ;
27
- import java .util .Collections ;
28
27
import java .util .Map ;
29
28
import java .util .Set ;
30
29
31
30
@ Component
32
31
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 +
36
37
"public class $CLASSNAME {\n " +
37
38
" /*\n " +
38
39
" * TODO:\n " +
@@ -46,6 +47,87 @@ public class DwlTransformTranslator implements MuleComponentToSpringIntegrationD
46
47
" }\n " +
47
48
"}" ;
48
49
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
+ "\t private String id;\n " +
60
+ "\t private String source;\n " +
61
+ "\t private String sourceType;\n " +
62
+ "\t private 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
+ "\t public 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
+ "\n public class $CLASSNAME {\n " +
90
+ triggermeshDWTransformationClass + "\n \n " +
91
+ "\t public static String transform(TmDwPayload payload) {\n " +
92
+ "\t \t String uuid = payload.getId();\n " +
93
+ "\t \t String url = System.getenv(\" K_SINK\" );\n " + // NOTE: K_SINK is the URL for the target transformation service
94
+ "\t \t HttpClient client = HttpClient.newHttpClient();\n " +
95
+ "\t \t HttpRequest.Builder requestBuilder;\n " +
96
+ "\t \t DataWeavePayload dwPayload = new DataWeavePayload();\n " +
97
+ "\t \t if (payload.getSourceType().contains(\" ;\" )) {\n " +
98
+ "\t \t \t dwPayload.input_content_type = payload.getSourceType().split(\" ;\" )[0];\n " +
99
+ "\t \t } else {\n " +
100
+ "\t \t \t dwPayload.input_content_type = payload.getSourceType();\n " +
101
+ "\t \t }\n " +
102
+ "\t \t dwPayload.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 \t dwPayload.spell = \" $DWSPELL\" ;\n " +
105
+ "\t \t dwPayload.input_data = payload.getPayload();\n " +
106
+ "\t \t String body;\n \n " +
107
+ "\t \t try {\n " +
108
+ "\t \t \t requestBuilder = HttpRequest.newBuilder(new URI(url));\n " +
109
+ "\t \t \t ObjectMapper om = new ObjectMapper();\n " +
110
+ "\t \t \t body = om.writeValueAsString(dwPayload);\n " +
111
+ "\t \t } catch (Exception e) {\n " +
112
+ "\t \t \t System.out.println(\" Error sending request: \" + e.toString());\n " +
113
+ "\t \t \t return null;\n " +
114
+ "\t \t }\n \n " +
115
+ "\t \t requestBuilder.setHeader(\" content-type\" , \" application/json\" );\n " +
116
+ "\t \t requestBuilder.setHeader(\" ce-specversion\" , \" 1.0\" );\n " +
117
+ "\t \t requestBuilder.setHeader(\" ce-source\" , payload.getSource());\n " +
118
+ "\t \t requestBuilder.setHeader(\" ce-type\" , \" io.triggermesh.dataweave.transform\" );\n " +
119
+ "\t \t requestBuilder.setHeader(\" ce-id\" , payload.getId());\n \n " +
120
+ "\t \t HttpRequest request = requestBuilder.POST(HttpRequest.BodyPublishers.ofString(body)).build();\n \n " +
121
+ "\t \t try {\n " +
122
+ "\t \t \t HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\n " +
123
+ "\t \t \t // TODO: verify the response status and body\n " +
124
+ "\t \t \t return response.body();\n " +
125
+ "\t \t } catch (Exception e) {\n " +
126
+ "\t \t \t System.out.println(\" Error sending event: \" + e.toString());\n " +
127
+ "\t \t \t return null;\n " +
128
+ "\t \t }\n " +
129
+ "\t }\n " ;
130
+
49
131
@ Override
50
132
public Class <TransformMessageType > getSupportedMuleType () {
51
133
return TransformMessageType .class ;
@@ -66,7 +148,8 @@ public DslSnippet translate(
66
148
return formExternalFileBasedDSLSnippet (component );
67
149
}
68
150
69
- return formEmbeddedDWLBasedDSLSnippet (component , Helper .sanitizeForBeanMethodName (flowName ), id );
151
+ //return formEmbeddedDWLBasedDSLSnippet(component, Helper.sanitizeForBeanMethodName(flowName), id);
152
+ return formTriggerMeshDWLBasedDSLSnippet (component , Helper .sanitizeForBeanMethodName (flowName ), id );
70
153
}
71
154
72
155
return noSupportDslSnippet ();
@@ -90,11 +173,52 @@ private DslSnippet formEmbeddedDWLBasedDSLSnippet(TransformMessageType component
90
173
replaceClassName (externalClassContentSuffixTemplate , className );
91
174
92
175
return DslSnippet .builder ()
93
- .renderedSnippet (replaceClassName (STATEMENT_CONTENT , className ))
176
+ .renderedSnippet (replaceClassName (TRANSFORM_STATEMENT_CONTENT , className ))
94
177
.externalClassContent (externalClassContent )
95
178
.build ();
96
179
}
97
180
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
+
98
222
private DslSnippet formExternalFileBasedDSLSnippet (TransformMessageType component ) {
99
223
String resource = component .getSetPayload ().getResource ();
100
224
String className = sanitizeForClassName (resource );
@@ -104,7 +228,7 @@ private DslSnippet formExternalFileBasedDSLSnippet(TransformMessageType componen
104
228
+ resource .replace ("classpath:" , "" )
105
229
+ replaceClassName (externalClassContentSuffixTemplate , className );
106
230
return DslSnippet .builder ()
107
- .renderedSnippet (replaceClassName (STATEMENT_CONTENT , className ))
231
+ .renderedSnippet (replaceClassName (TRANSFORM_STATEMENT_CONTENT , className ))
108
232
.externalClassContent (content )
109
233
.build ();
110
234
}
@@ -115,6 +239,18 @@ public static String sanitizeForClassName(String classNameCandidate) {
115
239
return (capitalizeFirstLetter (sanitizedClassName ) + "Transform" );
116
240
}
117
241
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
+
118
254
private boolean isComponentReferencingAnExternalFile (TransformMessageType component ) {
119
255
return component .getSetPayload ().getContent ().isEmpty ();
120
256
}
0 commit comments