I'm trying to make a general method for sending SOAP requests and getting responses. I'm programming using Groovy and I'm using the wslite library to help me out with SOAP. Here's a sample snippet for making a SOAP request and getting a response:
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import wslite.soap.*
SOAPClient client = new SOAPClient('http://www.dneonline.com/calculator.asmx')
def response = client.send(SOAPAction: 'http://tempuri.org/Add') {
body {
Add(xmlns: 'http://tempuri.org/') {
intA(x)
intB(y)
}
}
}
By general, I meant being able to dynamically create a SOAP request (given certain information such as the service/method name, the parameters contained in the method, etc.) and obtain the SOAP response. I'm thinking something like this:
#Grab('com.github.groovy-wslite:groovy-wslite:1.1.2')
import wslite.soap.*
def getResponse(String clientURL, String action, String service, String serviceNamespace, Map parameters, ...) {
SOAPClient client = new SOAPClient(clientURL)
def response = client.send(SOAPAction: action) {
body {
"$service"(xmlns: serviceNameSpace) {
...
}
}
}
}
My problem lies in constructing the closure for the request body. Like, in example, if my method received a service Add, a serviceNamespace http://tempuri.org/, and a parameter map like so: [intA: x, intB: y]... how do I merge all of these so that I can construct this kind of closure:
Add(xmlns: 'http://tempuri.org/') {
intA(x)
intB(y)
}
I'm pretty much a newbie to Groovy, so don't be too harsh. If there's a better way to implement this concept of a general method, I would gladly like to hear it. The concept is similar to this. But I'd rather play with Map than a String. I'm not using Grails, really. Just plain Groovy.
In short, cfrick is correct:
[intA: x, intB: y].each{fn,arg -> delegate."$fn"(arg) }
How does it work?
An easy way to see how this works is to simulate it with a fake client class:
groovy.util.NodeBuilder
class Client {
def send(String action, Closure closure) {
closure.delegate = new NodeBuilder()
closure()
}
}
def client = new Client()
def response = client.send('http://tempuri.org/Add') {
body {
Add(xmlns: 'http://tempuri.org/') {
intA(1)
intB(2)
}
}
}
assert response.Add[0].#xmlns == 'http://tempuri.org/'
assert response.Add.intA.text() == '1'
assert response.Add.intB.text() == '2'
In the example above, the response object is created by Groovy's NodeBuilder. It's just a quick way to prototype something that processes the closure passed to Client.send().
With this testable code I'll try what cfrick suggested and validate that it works:
def doIt(String action, String service, String serviceNamespace, Map params) {
def client = new Client()
client.send(action) {
body {
"$service"(xmlns: serviceNamespace) {
params.each { method, argument ->
delegate."$method"(argument)
}
}
}
}
}
response = doIt('http://tempuri.org/Add', 'Add', 'http://tempuri.org/', [intA: 1, intB: 2])
assert response.Add[0].#xmlns == 'http://tempuri.org/'
assert response.Add.intA.text() == '1'
assert response.Add.intB.text() == '2'
Request Body
In addition, you can factor out the process of creating the request body:
def getRequestBody(String service, String serviceNamespace, Map params) {
{ ->
"$service"(xmlns: serviceNamespace) {
params.each { method, argument ->
delegate."$method"(argument)
}
}
}
}
def doIt(String action, String service, String serviceNamespace, Map params) {
def client = new Client()
client.send(action) {
body(getRequestBody(service, serviceNamespace, params))
}
}
Related
I would like to write a REST API that return HTTP 400 when I try to create an already existing entity or when I try to update an non existing entity.
#Post
fun create(#Body entity: #Valid Entity): HttpResponse<Entity> {
val optional = entityService.find(entity)
if(optional.isPresent) {
return HttpResponse.badRequest()
}
return HttpResponse.created(entityService.save(entity))
}
How can I do that using non blocking endpoint with RXJava2 and Micronaut, I can only find examples with switchIfEmpty
#Post
#Status(HttpStatus.CREATED)
fun createMeal(#Body entity: #Valid Entity): Single<Entity> {
return entityService.find(entity)
.switchIfEmpty(entityService.save(entity))
.map{success -> entity}
}
but this code always return HTTP 200 even if nothing is saved, I don't think it's a good practice.
Thanks
You would use map to convert the entity to a bad request response since if it exists that is what you want to return. You could also use switchIfEmpty to save the entity which would only occur if the entity is not found. Ensure you wrap that code inside Flowable.defer to prevent the logic from executing no matter what. In your reactive example above the save will occur on every execution.
return entityService.find(entity)
.map(entity -> HttpResponse.badRequest())
.switchIfEmpty(Flowable.defer() -> {
//return a publisher that emits HttpResponse.created(entity)
})
Finally I do something like this :
fun update(name: String, entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(name)
.subscribe(
{
entity.name = name
update(entity, observable)
},
{ observable.onError(RuntimeException("Entity : $name doesn't exist")) }
)
return observable.singleOrError()
}
fun save(entity: Entity): Single<Entity> {
val observable = BehaviorSubject.create<Entity>()
entitysRepository.find(entity.name)
.subscribe(
{ observable.onError(RuntimeException("Entity : ${entity.name} already exist")) },
{ save(entity, observable) }
)
return observable.singleOrError()
}
Don't really know if it's a good practice or not.
I am new to spray and I am trying to write a custom directive. I would like the directive to reject the request if the header value is not valid otherwise leave the request alone.
I've tried to absorb this page:
http://spray.io/documentation/1.1.2/spray-routing/key-concepts/directives/
Specifically, the part about the responder chain. I'm trying to create something at the level of the bar Directive in the illustration. I'm just not getting how to pass the context unchanged to the inner route.
My else block below is not correct but expresses what I am trying to do. I just can't figure out how to implement it.
Any help would be greatly appreciated.
trait ApiKeyDirective {
import spray.routing.directives.HeaderDirectives._
import spray.routing.directives.BasicDirectives._
def validateApiKey(): Directive1 = {
headerValueByName("api-key") {key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
}
object ApiKeyDirective extends ApiKeyDirective
You can combine
headerValueByName:
def headerValueByName(headerName: String): Directive1[String]
with validate:
def validate(check: ⇒ Boolean, errorMsg: String): Directive0
For example:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
validate(key == "123", "Invalid API key") {
route
}
}
or without validate:
def validateApiKey(route: Route) =
headerValueByName("api-key") { key =>
if (key == "123")
route
else
reject(ValidationRejection("Invalid API key"))
}
Usage:
lazy val route = ...
... ~
pathPrefix("test_directive") {
get {
validateApiKey {
complete("ok")
}
}
} ~
...
Test from cmd/shell:
# curl http://localhost:8080/test_directive
Request is missing required HTTP header 'api-key'
# curl http://localhost:8080/test_directive -H 'api-key: bad'
Invalid API key
# curl http://localhost:8080/test_directive -H 'api-key: 123'
"ok"
I'm just not getting how to pass the context unchanged to the inner
route.
Spray does that for you!
Your code is mostly correct, there are just 2 simple problems to fix!
Firstly, you need to flatMap headerValueByName("api-key") directive.
Secondly, the return type will be Directive0 because the directive won't provide any value.
So final code would look like this:
object ApiKeyDirective {
import spray.routing.Directives._
val validateApiKey: Directive0 =
headerValueByName("api-key").flatMap { key =>
val valid = key == "123"
if (!valid) reject() else pass
}
}
Also, I recommend you to add a custom rejection to reject() block so that API users will be informed when their api key is invalid.
I have (formerly) REST spray.io webservice. Now, I need to generate SESSIONID in one of my methods to be used with some other methods. And I want it to be in the response header.
Basically, I imagine logic like the following:
path("/...") {
get {
complete {
// some logic here
// .....
someResult match {
case Some(something) =>
val sessionID = generateSessionID
session(sessionID) = attachSomeData(something)
// here I need help how to do my imaginary respond with header
[ respond-with-header ? ]("X-My-SessionId", sessionID) {
someDataMarshalledToJSON(something)
}
case None => throw .... // wrapped using error handler
}
}
}
}
But, it doesn't work inside complete, I mean respondWithHeader directive. I need an advice.
There is a respondWithHeader directive in Spray. Here is official doc and example of how you could use it:
def respondWithSessionId(sessionID: String) =
respondWithHeader(RawHeader("X-My-SessionId", sessionID))
path("/...") {
get {
// some logic here
// .....
sessionIDProvider { sessionID =>
respondWithMediaType(`application/json`) { // optionally add this if you want
respondWithSessionId(sessionID) {
complete(someDataMarshalledToJSON(something))
}
}
}
}
}
This is what the section of code looks like
get{
respondWithMediaType(MediaTypes.`application/json`){
entity(as[HttpRequest]){
obj => complete{
println(obj)
"ok"
}
}
}
}~
I can map the request to a spray.http.HttpRequest object and I can extract the uri from this object but I imagine there is an easier way to parse out the parameters in a get request than doing it manually.
For example if my get request is
http://localhost:8080/url?id=23434&age=24
I want to be able to get id and age out of this request
Actually you can do this much much better. In routing there are two directives: parameter and parameters, I guess the difference is clear, you can also use some modifiers: ! and ?. In case of !, it means that this parameter must be provided or the request is going to be rejected and ? returns an option, so you can provide a default parameter in this case. Example:
val route: Route = {
(path("search") & get) {
parameter("q"!) { query =>
....
}
}
}
val route: Route = {
(path("search") & get) {
parameters("q"!, "filter" ? "all") { (query, filter) =>
...
}
}
}
Documentation seems to be lacking on both the plugin side as well as the HTTPBuilder side of things. I'm trying to submit some json through the put method, but it keeps telling me that put() doesn't like the map I am feeding it.
Does anyone have an example of a PUT using the Grails REST Client plugin? Here is what I've tried:
withHttp(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent)
if (json.stat == 'ok') {
wsr.success = true
}
}
Error:
No signature of method: com.wbr.pano.PanService.put() is applicable for argument types: (java.util.LinkedHashMap) values: [[body:
{
"class":"com.wbr.platform.Pano",
"errorMessage":"null",
"imageSize":0,
"id":26,
"completed":"2011-03-20 3:50:27.257",
"downloading":"2011-03-20 3:49:12.269",
"processing":"2011-03-20 3:49:42.911",
"uploading":"2011-03-20 3:50:12.107"
}
]]
HTTPBuilder doesn't have a put method. Try changing withHttp to withRest so that your statements are executed with the RESTClient. Also, I think by default the body is encoded as URL encoded, so you might need to specify requestContentType: groovyx.net.http.ContentType.JSON as another parameter to your put.
import static groovyx.net.http.ContentType.*
withRest(uri: "http://foo/doo/roo") {
def bodyContent = [
pano: jsonText
]
def json = put(body: bodyContent, requestContentType: JSON)
if (json.status == 200) {
wsr.success = true
}
}