Search code examples
javaspringspring-integrationspring-messagingeai

Enriching Collection


I would like to enrich collection of orders with person details.

Suppose I've (example is in json):

[
   {
      "orderId": 123,
      "quantity": 5,
      "buyerId": "84aa820f-2301-4d01-8c4c-2b71204da7dd" 
   },
   {
      "orderId": 124,
      "quantity": 5,
      "buyerId": "7158a748-dfd0-47e5-b620-e8ca4d3ac84d" 
   }
]

And now I would like extract all buyer identifiers to hit other message endpoint, so I will get in result:

[
   {
       "personId": "84aa820f-2301-4d01-8c4c-2b71204da7dd",
       "name": "Johny",
       "surname": "Bravo"
   },
   {
       "personId": "7158a748-dfd0-47e5-b620-e8ca4d3ac84d",
       "name": "Felica",
       "surname": "Good"
   }
]

With this information I need to enrich each of the objects with person details, so the final result will be like this:

[
   {
      "orderId": 123,
      "quantity": 5,
      "buyerId": "84aa820f-2301-4d01-8c4c-2b71204da7dd",
      "buyer": {
         "personId": "84aa820f-2301-4d01-8c4c-2b71204da7dd",
         "name": "Johny",
         "surname": "Bravo"
      }
   },
   {
      "orderId": 124,
      "quantity": 5,
      "buyerId": "7158a748-dfd0-47e5-b620-e8ca4d3ac84d",
      "buyer": {
         "personId": "7158a748-dfd0-47e5-b620-e8ca4d3ac84d",
         "name": "Felica",
         "surname": "Good"
      }
   }
]

Is that possible to achieve this using enrichers?


Solution

  • Well, ContentEnricher is exactly for this reason. You receive a message, send enrich request, wait of reply and produce output.

    What you are asking about enriching JSON string is a bit unusual task and comes out of Spring Integration scope. I can only suggest to implement some service method to accept that JSON, to do parsing, iteration and modification. In the end of method you would produce a desired JSON. This one is really can be used from the @ServiceActivator to listen for the enrich request message channel and return reply to the replyChannel header.

    How to do a JSON parsing and so on seems for me is out of the question scope. However I can suggest to take a look into the JsonToObjectTransformer to get a List of some POJOs to iterate and modify. However a simple Map for each record is sufficient as well. For converting back from the List<Map> to JSON you can consider to use ObjectToJsonTransformer.

    UPDATE

    If you are able to covert that JSON to the List<POJO> or List<Map>, then the splitter-aggregator pattern is for you.

    I would make it like this, though:

        @Bean
        public IntegrationFlow enrichJson() {
            return f -> f
                    .transform(Transformers.fromJson(List.class))
                    .split()
                    .channel(c -> c.executor(Executors.newCachedThreadPool()))
                    .enrich(e -> e
                            .requestPayloadExpression("payload.buyerId")
                            .requestChannel(getBuyerChannel())
                            .propertyExpression("buyer", "payload"))
                    .aggregate()
                    .transform(Transformers.toJson());
        }
    

    UPDATE2

    If you'd prefer to obtain list of buyers in one go through the data base, for example, then let's consider this solution.

    If your original list is really JSON, we can do something like this:

         .enrich(e -> e
                        .requestPayloadExpression("#jsonPath(payload, '$.buyerId')")
                        .requestChannel(getBuyersChannel())
                        .headerExpression("buyers", "payload"))
    

    The jsonPath extracts all the buyerId properties and produces a List of their values.

    The getBuyersChannel flow must perform some query and return, example, a Map<String, String> - buyerId -> buyer_as_json. This result is going to be stored in the mentioned above buyers header.

    After this .enrich() you need to write a custom .transform() code and iterate over that buyers header and perform String.replaceFirst("(\" + buyer.getKey() + \",)", "$1" + buyer.getValue()).

    If your JSON is already List<POJO> or List<Map>, you can use similar approach but using Java 8 Streams:

    .<List<Map<?, ?>>>requestPayload(m ->
                m.getPayload()
                        .stream()
                        .map(entry -> entry.get("buyerId"))
                        .collect(Collectors.toList()))
    

    The result from the DB could be like Map<String, Map<?, ?>> and you can perform similar iteration to extend maps for orders with their buyers.