0

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?

Dariss
  • 1,258
  • 1
  • 12
  • 27

1 Answers1

1

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.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Well json was just an example, it can also be HashMap or POJO :) The main thing which bothers me, is how to extract using expression language all buyer ids and how to map it back when I receive reply message. If you could provide me with some example I would be really grateful :) – Dariss Mar 22 '18 at 19:33
  • 1
    See an UPDATE in my answer. – Artem Bilan Mar 23 '18 at 17:31
  • Thanks for the example, that would work great for commands, when the execution time is not required to be low. But what about situation with queries, when I need to do it in one step. Like I need to get all buyerIds at once and request all buyers with one message and then merge the reply message. Is that possible? – Dariss Mar 24 '18 at 20:23
  • Yes. Anything is possible with some custom code. I need to think more about that. But still pay attention how I parallel the work with an executor channel. Maybe that would be sufficient for you instead of one go for all buyers info – Artem Bilan Mar 24 '18 at 21:07
  • I thought this way of enriching is not something non-standard. I can add a list of buyers as {"orders": [], "buyers": []}, but I this solution is something I really wanted to avoid. – Dariss Mar 25 '18 at 08:27
  • Please, see an UPDATE2 in my answer. – Artem Bilan Mar 27 '18 at 14:13
  • wow, impressive, this will do the work :) Thank you very much for help – Dariss Mar 28 '18 at 05:02