Data Mapping
The following tutorial uses the example project codebase. Make sure you have the repository cloned so you can follow along and run the tests.
This example is identical to the first simple REST example.
However, it abstracts the map
logic so it can be unit tested outside of the flow.
It’s recommended to unit test data mappings on their own, since mappings can become complex.
Creating the Map
Unlike the previous tutorials, we won’t start with the flow specification. First, we will create and unit test the mapping logic. It is much easier to develop mappings in isolation instead of continually deploying them to the test server to verify that they work.
Open the Kotlin file located at main/kotlin/flowexamples/e04/E04SimpleMapping.kt
.
The E04SimpleMapping
object contains the following simpleMapping
variable:
val simpleMapping = """
{
"message" : #input.payload.value
}
""".trimIndent()
Mappings are written as strings that define the output of the processor in JSON format. Mappings are based on the JSONiq query language, so a lot of what you can do in native JSONiq can also be accomplished here. In this case, our mapping simply returns a JSON object with a "message" property set to the "value" property of the incoming payload.
To get a better sense of how this mapping works, try running the following example by selecting the "Try It" button below:
{
"message" : #input.payload.value
}
{ "value" : "testData" }
Refer to the Data Mapping documentation for more examples that you can run directly in the browser.
Testing the Map
Open the Kotlin class located at test/kotlin/flowexamples/e04/E04SimpleMappingTest.kt
.
Here, we test the mapping in isolation without actually deploying a flow.
First, we set up a mapping test context as the following code demonstrates:
val mappingConfig = mapConfig {
mapSpec = simpleMapping
}
withMap(mappingConfig) {
...
}
In the withMap()
context, we use the input()
function to create the following input
variable:
val input = input(mapOf("value" to "testData"))
The input()
function creates a FlowProcessorInput
object with a payload
property equal to the argument passed to it. Our argument happens to be a mapOf()
, so the payload
becomes {value=testData}
.
Next, we pass the input
variable into the following map()
function:
val mappingResult = map(input)
The map()
function runs the mapping logic based on the current context.
Because we assigned simpleMapping
to the MapConfig
object’s mapSpec
property, map()
will use the mapping logic defined in the simpleMapping
variable.
Remember that we’re working with JSON compliant objects, so it might be easier to visualize the mapping with the following table:
input.payload | mappingResult |
---|---|
|
|
Lastly, we assert the results of the map with following line:
assertThat(mappingResult).isEqualTo(mapOf("message" to "testData"))
mappingResult
and the hardcoded mapOf()
will both equal {message=testData}
, and so the test passes.
Detailed information on unit testing the map processor can be found in the testing section in the map processor. |
Creating the Flow
Now that we have a fully tested mapping config, we can use it within a flow. Open the Kotlin file located at main/kotlin/flowexamples/e04/E04SimpleMappingFlow.kt
. The entry point for this flow is a familiar-looking restApi
processor, as the following code shows:
val simpleMappingSpec = flowConfig {
id = "simple-mapping"
description = "Simple Mapping Flow"
ownerId = OWNER_ID
exchangePattern = RequestResponse
restApi {
id = "mapping-api"
apiSpecId = simpleMappingResourceKey.toResourceIdentifier()
}
...
}
The next step in the flow is the following map
processor:
map {
id = "map-response"
mapSpec = simpleMapping
}
This processor maps the payload from the restApi
processor and, since it is the last processor in the flow, sends the mapped results back as the response.
Testing the Flow
Open the Kotlin class located at test/kotlin/flowexamples/e04/E04SimpleMappingFlowTest.kt
. This test is almost identical to previous tests that we’ve looked at, as the following code shows:
flowTest(ctx) {
val inputValue = "testValue"
val responseMessage = restApiEndpoint(simpleMappingSpec)
.path("echo")
.request()
.basicAuth()
.post(json(SimpleValue(inputValue)), SimpleMessage::class.java)
assertThat(responseMessage.message).isEqualTo(inputValue)
}
To recap, the test uses restApiEndpoint()
to make a POST request to the flow’s /echo
endpoint. The SimpleValue
data class formats the request body as {"value":"testValue"}
.
On the server, the map
processor maps "value" to "message" and returns {"message":"testValue"}
.
We specify that we expect a "message" property in the response using the SimpleMessage
data class.
Then the assertion verifies if the value on the response is still the same.
If you’d like more practice, try mapping nested objects and lists (e.g., people[[1]].name
), or move on to the next tutorial on SOAP APIs.