1

I have created an API using Ratpack and Groovy. I want a POST API such that the data should be processed and stored in 2 cassandra databases say table-A and table-B. For Now I have this in my Ratpack.groovy, and thus I have to call both the APIs whenever a data has to be pushed:

prefix("A") {
    post {  registry.get(PostEndpointA).handle(context) }
}
prefix("B") {
    post {  registry.get(PostEndpointB).handle(context) }
}

I wanted a single POST API Call like this, so that by single API call the request can be delegated to both the endpoints together:

prefix("post") {
    post {  registry.get(PostEndpointA).handle(context) }
    post {  registry.get(PostEndpointB).handle(context) }
}

OR, I want this:

prefix("post") {
        post {  registry.get(PostEndpoint).handle(context) }
}

And in the PostEndpoint, I can perform both the operations as this:

saveJsonAsA(context)
            .promiseSingle()
            .then { ItemA updatedItem ->
                context.response.headers
                    .add(HttpHeaderNames.LOCATION, "/item/api/A")
                context.response.status(HttpResponseStatus.CREATED.code())
                    .send()
            }
saveJsonAsB(context)
            .promiseSingle()
            .then { ItemB updatedItem ->
                context.response.headers
                    .add(HttpHeaderNames.LOCATION, "/item/api/B")
                context.response.status(HttpResponseStatus.CREATED.code())
                    .send()
            }

In both the cases, the item is added to only table-A and not B or whatsoever is written in the code earlier.

Note That ItemA and ItemB relates to essentially same DB, only the primary keys are different, so as to facilitate the GET from 2 ways. Any idea how to do this in Ratpack?

tanmayghosh2507
  • 773
  • 3
  • 12
  • 31
  • Can you provide your requirements as a series of request/response expectations? That is POST /endpointA -> some thing should happen POST /endpointB -> something else GET /endpointA -> something more – Dan Hyun Jun 06 '16 at 17:16
  • POST: endpointA -> parsing the request JSON and saving the objects into table A. endpointB -> parsing the request JSON and saving the objects into table B. GET: /api/A/123 -> returns all attributes from table A where identifier=123 /api/B/123 -> returns all attributes from table A where identifier=123 The GET Part is okay, but I am having problem in handling the multiple POST parts. – tanmayghosh2507 Jun 06 '16 at 17:24
  • Can anyone help me with this? – tanmayghosh2507 Jun 24 '16 at 11:53

2 Answers2

1

If I'm understanding this correctly you could try doing something similar to this:

@Grab('io.ratpack:ratpack-groovy:1.3.3')

import static ratpack.groovy.Groovy.ratpack

import ratpack.http.Status
import ratpack.exec.Promise
import com.google.common.reflect.TypeToken

class ServiceA {
  Promise<String> save(json) {
    Promise.value(json)
  }
}

class ServiceB {
  Promise<String> save(json) {
    Promise.value(json)
  }
}

ratpack {
  bindings {
    bindInstance new ServiceA()
    bindInstance new ServiceB()
  }
  handlers {
    post(':name') { // parameterize on path token
      def name = pathTokens.get('name') // extract token
      def service = null
      switch(name) {
        case 'A':
          service = get(ServiceA) // extract appropriate service
          break
        case 'B':
          service = get(ServiceB) // extract appropriate service
          break
      }

      parse(new TypeToken<Map<String, Object>>(){})
        .flatMap(service.&save) // save your extracted item
        .then {
          response.headers.add('Location', "/item/api/$name")
          response.status(Status.of(201))
          response.send()
        }
    }
  }
}

Sample curls look like this

$ curl -X POST -H'Content-Type: application/json' -d '{"foo":"bar"}' -v localh ost:5050/A

< HTTP/1.1 201 Created
< Location: /item/api/A
< content-length: 0
< connection: keep-alive

$ curl -X POST -H'Content-Type: application/json' -d '{"foo":"bar"}' -v localh ost:5050/B

< HTTP/1.1 201 Created
< Location: /item/api/B
< content-length: 0
< connection: keep-alive

From the volume of questions you have been posting I recommend signing up for the Ratpack community slack channel https://slack-signup.ratpack.io/

Dan Hyun
  • 536
  • 3
  • 7
  • I joined the Ratpack Community Slack Channel. Thats a lot for your effort and time, your solution is really helpful, which I can use later. But for now, it was not my problem. I want to always POST to 2 different tables, it is not condition dependent. I mean whenever there is any POST API call, data should be written to both table A and B. Right now, it is not writing data to both the tables, but if the code/handler of inserting into table A is written earlier, only insertion into A is happening and not B. – tanmayghosh2507 Jun 06 '16 at 19:25
  • I would take a look at rxJava/rxGroovy's Observable.zip(). This is what netflix uses. – Kango_V Feb 01 '17 at 18:27
0

We use rxJava to do this: Single .zip( service.add(cv1), service.add(cv2), (a, b) -> new Object[] { a, b } ) .blockingGet(); We wrap that in a promise and process the array in the then block.

Kango_V
  • 1,720
  • 2
  • 15
  • 11