I would like to pluck documents from a JSON payload and send them to different processors based on their location.
JSON:
{
"email" : {
"documents" : [{
"name" : "Document 1",
"id" : "1111"
}, {
"name" : "Document 2",
"id" : "222"
}
]
},
"sms" : {
"documents" : [{
"name" : "Document 3",
"id" : "3333"
}, {
"name" : "Document 4",
"id" : "4444"
}
]
}
}
I was thinking to achieve this by doing something like this:
from("servlet:///doc").unmarshal()
.json(JsonLibrary.Jackson, DocumentRequest.class)
.split().method("docSplit", "split")
.choice()
.when().header("mode").isEqualTo("email")
.to("direct:email")
.when().header("mode").isEqualTo("sms")
.to("direct:sms");
My splitter can receive a DocumentRequest and pull out all the docs... but I do not know how to set the "mode" header for future routing.
How can I set the "mode" header?
Is there a better overall approach?
You can always put custom split logic in a custom processor and use ProducerTemplate
For example:
from("servlet:///doc").unmarshal()
.json(JsonLibrary.Jackson, DocumentRequest.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
ProducerTemplate producer=exchange.getContext().createProducerTemplate();
String mode;
for (Document doc: // split and set mode logic goes here ) {
if (mode.compareToIgnoreCase("email") ==0) {
producer.sendBody("direct:email", doc);
} else
if (mode.compareToIgnoreCase("sms") ==0) {
producer.sendBody("direct:sms", doc);
}
...
}
}
});
Related
I encountered a problem which is very challenging to my Angular level. Could you give a help please?
In Spring Data REST the entity Worker has a #OneToMany bidirectional relationship with the entity TempworkEvent, shown below under _links. I would like to access TempworkEvent objects and their attributes through this relationship.
{
"id" : 3,
"name" : "Nadja Miller",
"profession" : "Experte/in Anästhesiepflege",
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/workers/3"
},
"worker" : {
"href" : "http://localhost:8080/api/workers/3"
},
"tempworks" : {
"href" : "http://localhost:8080/api/workers/3/tempworks"
},
"tempworkEvents" : {
"href" : "http://localhost:8080/api/workers/3/tempworkEvents"
}
}
In Angular the Worker entity successfully:
getWorkers(): Observable<Worker[]> {
return this.httpClient.get<GetResponseWorkers>(this.workersUrl).pipe(
map(response => response._embedded.workers)
);
}
(...)
interface GetResponseWorkers {
_embedded: {
workers: Worker[];
}
}
The challenge is how to access the Worker's tempworkEvents object and its attributes through _links:
"tempworkEvents" : {
"href" : "http://localhost:8080/api/workers/3/tempworkEvents"
}
One of:
Make an additional network request to get the TempworkEvents
Don't have a TempworkEvent JPA repository, and Spring Data Rest will include the tempworkEvents data in the workers response
or, use a Projection, like:
in entities/projections/MyWorkerProjection.java:
#Projection(name="foo", types={Worker.class})
public interface MyWorkerProjection {
Long getId();
String getName();
String getProfession();
List<TempworkEvent> getTempworkEvents();
}
Then call http://localhost:8080/api/workers/3?projection=foo
If you want to have "auto" projections, see: Spring Data REST: projection representation of single resource
You may want to create another interface for TempworkEvent, if you want to project its data as well as its links
let obj = {
"id" : 3,
"name" : "Nadja Miller",
"profession" : "Experte/in Anästhesiepflege",
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/workers/3"
},
"worker" : {
"href" : "http://localhost:8080/api/workers/3"
},
"tempworks" : {
"href" : "http://localhost:8080/api/workers/3/tempworks"
},
"tempworkEvents" : {
"href" : "http://localhost:8080/api/workers/3/tempworkEvents"
}
}
}
console.log('tempworkEvents',obj._links.tempworkEvents);
You can access the object like above.
I am trying to build a pact between two services using asynchronous communication.
This is the code I used for generate the pact:
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "provider", providerType = ProviderType.ASYNCH)
public class StringifiedPactTest {
#Pact(consumer = "consumer", provider = "provider")
public MessagePact generatePact(MessagePactBuilder builder) {
return builder.hasPactWith("provider")
.expectsToReceive("A valid aws sns event")
.withContent(new PactDslJsonBody().stringType(new String[]{"MessageId", "TopicArn"}).stringValue("Message", new PactDslJsonBody().stringType("Value", "Foo").toString()))
.toPact();
}
#Test
#PactTestFor(pactMethod = "generatePact")
public void buildPact(List<Message> messages) {
}
}
And the generated pact is
{
"consumer": {
"name": "consumer"
},
"provider": {
"name": "provider"
},
"messages": [
{
"description": "A valid aws sns event",
"metaData": {
"contentType": "application/json"
},
"contents": {
"TopicArn": "string",
"Message": "{\"Value\":\"Foo\"}",
"MessageId": "string"
},
"matchingRules": {
"body": {
"$.MessageId": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.TopicArn": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.10"
}
}
}
This means the producer should have a "Message" that matches {"Value" : "Foo"}, any other combination like {"Value" : "Bar" } won't be successful.
Is there any way to add matching rules inside a stringified json?
Thanks!
Here's an anonymised example from a test we have. Hope it's useful. This creates a pact that matches only on type. So on the provider side, when I test against the contract, it doesn't matter what value I have for categoryName for example, as long as it's a stringType:
#PactTestFor(providerName = "provider-service", providerType = ProviderType.ASYNCH)
public class providerServiceConsumerPactTest {
private static String messageFromJson;
#BeforeAll
static void beforeAll() throws Exception {
messageFromJson = StreamUtils.copyToString(new ClassPathResource("/json/pact/consumer-service_provider-service.json").getInputStream(), Charset.defaultCharset());
}
#Pact(provider = "provider-service", consumer = "consumer-service")
public MessagePact providerServiceMessage(MessagePactBuilder builder) {
DslPart body = new PactDslJsonBody()
.object("metaData")
.stringType("origin", "provider-service")
.datetimeExpression("dateCreated", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.closeObject()
.minArrayLike("categories", 0, 1)
.stringType("id", "example data")
.stringType("categoryName", "example data")
.booleanType("clearance", false)
.closeObject()
.closeArray();
return builder
.expectsToReceive("a provider-service update")
.withContent(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "providerServiceMessage")
public void testProviderServiceMessage(MessagePact pact) {
// State
final String messageFromPact = pact.getMessages().get(0).contentsAsString();
// Assert
JSONAssert.assertEquals(messageFromPact, messageFromJson, false);
}
I'm having exactly the same issue, and unfortunately I don't think it's possible to tell Pact to parse the stringified JSON and look inside it (e.g. to verify that parse(Message).Value === "Foo" in your example).
The best you can do is write a regular expression to match the string you're expecting. This kind of sucks because there's no easy way to ignore the ordering of the JSON keys (e.g. "{\"a\":\"1\", \"b\":\"2\"}" and "{\"b\":\"2\", \"a\":\"1\"}" will compare different) but AFAIK Pact simply lacks the parsing functionality we're looking for, so the only tool it provides is regex.
Lets say we have json looks like
{
"name":"abc",
"lastName":"xyz",
"description":"aaaaa aaaa",
"dob":11-10-1988,
"workInformation":[
{
"address":"kolkata",
"workFor":"vvv Pvt Ltd",
"reference" : [
{
"refName" : "ttt",
"refId" : "12345"
},
{
"refName" : "sss",
"refId" : "23412"
}
]
},
{
"address":"bangalore",
"workFor":"www Pvt Ltd",
"reference" : [
{
"refName" : "rrr",
"refId" : "43434"
},
{
"refName" : "yyyy",
"refId" : "34213"
}
]
},
{
"address":"delhi",
"workFor":"sss Pvt Ltd",
"reference" : [
{
"refName" : "qqqq",
"refId" : "76767"
},
{
"refName" : "gggg",
"refId" : "65432"
}
]
}
]
}
I have tried insertFragment of DocumentPatchBuilder. Using this i am able to update before/after of the json properties. But i have to insert inside the property workInformation which is of array type. Here is the insertFragment example which i tried -
DocumentPatchBuilder pb = docMgr.newPatchBuilder();
pb.pathLanguage(DocumentPatchBuilder.PathLanguage.JSONPATH);
ObjectMapper mapper = new ObjectMapper();
pb.insertFragment("workInformation", Position.BEFORE,mapper.createObjectNode().put("hello", "hai"));
I wanted to insert data mentioned below inside the workInformation section using java api -
{
"address":"Mumbai",
"workFor":"zzz Pvt Ltd"
}
Please let me know how to do it.
Thanks for Reading.
To insert inside the workInformation array, you would need to specify the position as POSITION.LAST_CHILD to insert it as the last child of the child list of the context. Also, you would need to specify "workInformation" as an array type in the first argument of patchBuilder.insertFragment - ["workInformation"]. The code would be something like:
DocumentPatchBuilder pb = docMgr.newPatchBuilder();
pb.pathLanguage(DocumentPatchBuilder.PathLanguage.JSONPATH);
ObjectMapper mapper = new ObjectMapper();
ObjectNode fragmentNode = mapper.createObjectNode();
fragmentNode = mapper.createObjectNode();
fragmentNode.put("address", "mumbai");
fragmentNode.put("workFor", "zzz Pvt Ltd");
String fragment = mapper.writeValueAsString(fragmentNode);
pb.insertFragment("$.[\"workInformation\"]", Position.LAST_CHILD, fragment);
This should do the trick and this would insert
{
"address":"Mumbai",
"workFor":"zzz Pvt Ltd"
}
at the end of "workInformation" property.
I have defined a #Highlight query on my Solr repository class which returns the interesting 'snipplets' info if I call it from another class/controller. However if I access the method through the implicit rest service exposed via #RepositoryRestResource ( e.g. http://localhost:8080/modules/search/findByDescriptionContaining?description=maths ) , it doesn't return any highlighting/snipplet info at all
#RepositoryRestResource
interface ModuleRepository extends SolrCrudRepository<Module, String>{
#Highlight(prefix = "<b>", postfix = "</b>")
HighlightPage<Module> findByDescriptionContaining(#Param(value = "description") String description, Pageable pageable)
}
so to recap, calling the auto exposed method will return this:
{
"_embedded" : {
"modules" : [ {
"name" : "science",
"description" : "a somewhat scientific course with some maths",
"_links" : {
"self" : {
"href" : "http://localhost:8080/modules/102"
},
"module" : {
"href" : "http://localhost:8080/modules/102"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/modules/search/findByDescriptionContaining?description=maths"
}
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
how can I get the highlighting info using the method above ?
I'm looking for a best way to design a custom JSON response for RESTful API in Grails based on this presentation Design Beautiful REST + JSON APIs by Les Hazlewood.
Here is my Domain class
class TaxiType {
Date dateCreated, lastUpdated
String description
User createdBy
static hasMany = [taxis: Taxi]
static constraints = {
}
}
The desired response format for list is
{
"meta": {
"href": "https://api.mydomain.com/taxi-types",
"otherData": "..."
},
"paging": {
"offset": 0,
"limit": 10,
"total": 100,
"first": "https://api.mydomain.com/taxi-types?offset=0&limit=10",
"previous": null,
"next": "https://api.mydomain.com/taxi-types?offset=90&limit=10",
"last": "https://api.mydomain.com/taxi-types?offset=90&limit=10"
},
"data": [
{
"href": "https://api.mydomain.com/taxi-types/1",
"id": 1,
"description": "description 1",
"taxis": {
"href": "https://api.mydomain.com/taxi-types/1/taxis"
}
},
...
]
}
TaxiTypeController.groovy
def index(Integer limit) {
params.max = Math.min(limit ? : 10, 100)
params.offset = params ? .offset ? .toInteger()
withFormat {
json {
respond TaxiType.list(params),
[includes : includeFields,
paging : [total : TaxiType.count(), limit : params ? .max, offset : params ? .offset ? : 0]
]
}
}
}
private getIncludeFields() {
params.fields ? .tokenize(', ')
}
SumoJsonCollectionRenderer.groovy
class SumoJsonCollectionRenderer extends JsonCollectionRenderer {
SumoJsonCollectionRenderer(Class componentType) {
super(componentType)
}
public SumoJsonCollectionRenderer(Class componentType, MimeType...mimeTypes) {
super(componentType, mimeTypes)
}
# CompileStatic(SKIP)
# Override
protected void renderJson(object, RenderContext context) {
log.debug(object)
log.debug(object.size())
log.debug(object.getTotalCount())
Map tObject = ['data' : object]
if (context.arguments ? .paging) {
tObject['paging'] = context ? .arguments ? .paging
}
super.renderJson(tObject, context)
}
}
Required features are:
1) API users should be able to get only the required fields (Partial representations)
GET https://api.mydomain.com/taxi-types??fields=id,description,taxis
the desired response for this request should be
{
"meta" : {...}
"paging" : {...}
"data" : [{
"href" : "https://api.mydomain.com/taxi-types/1",
"id" : 1,
"description" : "Taxi Type1",
"taxis" : [{
"href" : "https://api.mydomain.com/taxis/123"
},
...
]
},
...
]
}
What I'm getting was
{
"data" : [{
"id" : 1,
"description" : "Taxi Type1",
"taxis" : [{
"class" : "com.domain.project.Taxi",
"id" : 1
}
]
},
...
],
"paging" : {
"total" : 80,
"limit" : 10,
"offset" : 0
}
}
I referred answer to this question Render metadata for pagination from Grails RestfulController index/search actions.
But still need to include links for first, previous, next & last in paging as mentioned in the desired format above.
2) Customizing output
for example
the taxis property with hasMany relationship should be rendered by default as
"taxis": {
"href": "https://api.mydomain.com/taxis/12345"
}
and if the user prefer to expand taxis property, for eg: GET /taxi-types?expand=taxis, the JSON format should be
"taxis": {
"href": "https://api.mydomain.com/taxis/12345"
"name": "Taxi name",
"type": "https://api.mydomain.com/taxi-types/1"
...
}
3) Add meta object to all responses
"meta": {
"href": "https://api.mydomain.com/taxi-types",
...
}
I tried Json Marshaller to customize the response. Here is my Marshaller code
JSON.registerObjectMarshaller(TaxiType) {TaxiType taxiType->
return [
id : taxiType ? .id,
description : taxiType ? .description
]
}
but it always renders with those two properties in return array id & description, even if I want some other properties like taxis.
How above 3 requirements can be implemented?. Thanks in advance.
I've created a plugin for grails 3 which allows you to add pagination, custom metadata and other useful features to your apis. You can add HAL features using LinkGenerators as well with my plugin.
well the Beapi-API-Framework plugin is the #1 api plugin for grails and provides pagination by default. See the github repo for a complete list of all functionality, documentation and install instructions