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
{
  "value": "testData"
}
{
  "message": "testData"
}

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.