HTTPBuilder & Session ID - groovy

I have the following code to connect to a REST API service, authenticate, retrieve a session ID then make further requests passing the session ID to authenticate. The initial request works and I get a HTTP 200 OK plus the session ID in the response, however when I try to make a second request passing the session ID in the header, I get
Caught: groovyx.net.http.HttpResponseException: Bad Request
I know the script can be written much better with the use of classes and try / catch etc. I am still learning both java and groovy so I start by just trying to do everything within the same class.
Any help much appreciated.
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.URIBuilder
import static groovyx.net.http.Method.POST
import static groovyx.net.http.ContentType.*
def url = 'https://1.1.1.1/web_api/'
def uri = new URIBuilder(url)
String CHKPsid
uri.path = 'login'
def http = new HTTPBuilder(uri)
http.ignoreSSLIssues()
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
body = [
"user":"username",
"password":"password"
]
response.success = { resp, json ->
println (json)
CHKPsid = (json.sid)
println "POST Success: ${resp.statusLine}"
}
}
uri.path = 'show-changes'
http.request(POST,JSON ) { req ->
headers.'Content-Type' = 'application/json'
headers.'X-chkp-sid' = '${CHKPsid}'
body = [
"from-date" : "2017-02-01T08:20:50",
"to-date" : "2017-10-21"
]
response.success = { resp, json ->
println (json)
println "POST Success: ${resp.statusLine}"
}
}

String interpolation does not work with single (or triple single quotes). When groovy will evaluate '${CHKPsid}' (single quotes), its value will be ${CHKPsid} (this string). In order to use the value of the variable, you should use double quotes: "${CHKPsid}" or simply just the variable: headers.'X-chkp-sid' = CHKPsid.
So the output of this:
String CHKPsid = "abc123"
println '${CHKPsid}'
println "${CHKPsid}"
will be:
${CHKPsid}
abc123
In order to quickly test what the server receives, you can use httpbin.org or requestb.in

So as well as the correct assignment of the value of the session ID, I found that calling the same HTTPbuilder - http.request the second time even with a change of uri, header and body was the problem. The listening server still saw this as part of the same login API call. My workaround / resolution was to define a 2nd HTTPbuilder with a different name and this now works. I'm interested to know if this is normal behaviour and how others approach this. Thanks.

Related

Using groovy script HttpBuilder request object, i need to validate the AEM node existence in JCR via http calls

Using groovy script, i am using HTTPBuilder request object: i need to check whether the AEM Parent node exist and create.
Details:
I am using this CLI script http://www.tothenew.com/blog/content-migration-in-aem-using-slingpostservlet/ to create nodes while migrating contents from XML. In the XML file i have a field which contains the full path of the parent node. I need to verify whether that node exist or not. If not i need to create that node. Kindly suggest.
Since you are using groovy and httpbuilder module, you could simply check if a Http GET on the complete resource path url with a json extention returns a HTTP 200 or a HTTP 404. HTTP 200 means the resource exists, 404 means the node does not exist.
Following is an simple example to determine if a JCR node exists or not. Kindly note that the code below does not create a missing node and is for illustration purpose only.
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.JSON
HTTPBuilder local = new HTTPBuilder("http://localhost:4502")
local.auth.basic("<USERNAME>", "<PASSWORD>")
def getStatus = {
resourcePath ->
try {
local.get(
path: resourcePath + ".json",
contentType: JSON
) {
resp, reader ->
return resp.status
}
} catch (HttpResponseException httpResponseException) {
return httpResponseException.statusCode
}
}
def nodePath = '/content/geometrixx-outdoors/en/men'
def httpNodeStatus = getStatus(nodePath);
if (httpNodeStatus == 404) {
println('node : ' + nodePath + ' does not exist. creating node')
// Code to create node
} else if (httpNodeStatus == 200) {
println('node : ' + nodePath + ' exists')
// Node exists
} else {
println('HTTP Status for node : ' + httpNodeStatus)
// handle other response codes
}
You could use sling's ResourceUtil methods to do this for you. Look at linked java docs. Following method can come handy for this -
public static Resource getOrCreateResource(ResourceResolver resolver,
String path,
Map<String,Object> resourceProperties,
String intermediateResourceType,
boolean autoCommit)
throws PersistenceException

SoapUI. Add multipart body part using groovy

I need to add json as multipart body part to "multipart/form-data" request using groovy.
I can do this using file attachments:
testRunner.testCase
.testSteps["/job/result"]
.getHttpRequest()
.attachBinaryData(json.toString().getBytes(), "application/json").contentID = "info"
My problem is "attachBinaryData" creates a temp file per request. It is not good for load tests :)
Is there other possibility to add body parts, without file attachments?
something like :
testRunner.testCase
.testSteps["/job/result"]
.getHttpRequest()
.addBodyPart("application/json", json.toString())
P.S. it must be the "add", because request has also one static attachment.
If you want to add a json to your request as a multipart/form-data using groovy script testStep you can use the follow code:
def jsonStr = "{'id':'test','someValue':'3'}"
def testStep = context.testCase.testSteps["/job/result"]
// set the content for the request
testStep.getHttpRequest().setRequestContent(jsonStr)
// and set the media type
testStep.testRequest.setMediaType('application/json')
// if you want to send as a multipart/form-data then use follow line instead
// testStep.testRequest.setMediaType('multipart/form-data')
This results in a request configured as follows:
Hope it helps,
Have found a solution using https://github.com/jgritman/httpbuilder
def http = new HTTPBuilder(serviceEndPoint)
def scanResultFile = new File(testRunner.testCase.getPropertyValue("ScanResultFile"))
http.request( POST ){ req ->
headers.'Connection' = 'Keep-Alive'
headers.'User-Agent' = 'SoapUI 4.5.1'
requestContentType = 'multipart/form-data'
ByteArrayBody bin = new ByteArrayBody(scanResultFile.readBytes(), "application/octet-stream", "jobResult");
StringBody info = new StringBody(testRunner.testCase.getPropertyValue("JsonScanResult"), "application/json", java.nio.charset.StandardCharsets.UTF_8);
MultipartEntity entity = new MultipartEntity()
entity.addPart("info", info);
entity.addPart("jobResult", bin)
req.entity = entity
}

Is it safe to reuse Groovy HTTPBuilder objects for multiple requests?

I have some HTTPBuilder code that behaves differently depending on whether or not I reuse the same HTTPBuilder object to perform two different requests to the same REST service:
def http = new HTTPBuilder( 'https://myBaseURI/' )
http.auth.basic username, password.getPlainText()
http.ignoreSSLIssues()
http.request(GET,JSON) { req ->
uri.path = 'some/api/path/'
headers.'User-Agent' = 'Mozilla/5.0'
} // this request always behaves as expected
http.request(POST, JSON) { req ->
uri.path = 'some/other/api/path'
headers.'User-Agent' = 'Mozilla/5.0'
body = {
// Request body elided for brevity
}
}
The 'correct' behavior is for the POST to return a 201 - Created, but the response comes back as 200 OK unless I create a new HTTPBuilder to handle issuing the second request, in which case, the API call behaves as expected.
Certainly, the cause of the different results could be elsewhere, but I first wanted to make sure I wasn't misusing this object. Are there problems to be aware of when reusing the HTTPBuilder to issue multiple HTTP requests?
Try removing that forward slash at the end when you set uri.path in the GET request.
Looking at the documentation for setPath in URIBuilder you get:
//Set the path component of this URI. The value may be absolute or relative to the current path. e.g.
def uri = new URIBuilder( 'http://localhost/p1/p2?a=1' )
uri.path = '/p3/p2'
assert uri.toString() == 'http://localhost/p3/p2?a=1'
uri.path = 'p2a'
assert uri.toString() == 'http://localhost/p3/p2a?a=1'
uri.path = '../p4'
assert uri.toString() == 'http://localhost/p4?a=1&b=2&c=3#frag'
I understand this to mean that if you set the uri.path of an httpbuilder object with a slash at the end, you have essentially updated the working path so any subsequent relative path updates to uri.path will result in a concatenation of the path. Therefore, your POST in that example ends up pointing at https://myBaseURI/some/api/path/some/other/api/path

How to read cookies in Scala play?

I'm adding Cookie like so:
Redirect(routes.UserPage.form(usersignin.email)).withCookies(Cookie("guid", md5hash1cookie))
How could i read it?
If i use:
println(Http.Request.current().cookies.get("guid"));
and i get error:
not found: value Http
UPD 1:
Correct way to use is
def form(msg: String = "") = Action {
request => {
// guid = guId.toString();
println(request.cookies.get("guid"));
...
}
}
How to get value out of coockie? println(request.cookies.get("guid")); returns Some(Cookie(guid,7a3bdea2ba59a196c02fb7bdbcdb4e26,None,/,None,false,false))
and i need just 7a3bdea2ba59a196c02fb7bdbcdb4e26 returned as string.
Solution:
for(gu <- request.cookies.get("guid")){
println(gu.value);
}
You don't have a shared state containing the requests in the Scala Play APIs, instead you will have to use the Action { request => way to define actions, on the request you can then access the cookies through request.cookies

Why is HTTPBuilder basic auth not working?

The following code doesn't authenticate the user (no authentication failure happens, but the call fails due to lack of permissions):
def remote = new HTTPBuilder("http://example.com")
remote.auth.basic('username', 'password')
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
response.success = { resp, json ->
json ?: [:]
}
}
But the following works fine:
def remote = new HTTPBuilder("http://example.com")
remote.request(POST) { req ->
uri.path = "/do-something"
uri.query = ['with': "data"]
headers.'Authorization' =
"Basic ${"username:password".bytes.encodeBase64().toString()}"
response.success = { resp, json ->
json ?: [:]
}
}
Why isn't the first one working?
Two things that I can think of off the top of my head.
The .setHeaders method requires a map. Have you tried 'Authorization' : "Basic ${"username:password".bytes.encodeBase64().toString()}" ?
If not, It's a bit more work and code, but you could user the URIBuilder as well. Generally I encapsulate to a different class
protected final runGetRequest(String endpointPassedIn, RESTClient Client){
URIBuilder myEndpoint = new URIBuilder(new URI(Client.uri.toString() + endpointPassedIn))
HttpResponseDecorator unprocessedResponse = Client.get(uri: myEndpoint) as HttpResponseDecorator
def Response = unprocessedResponse.getData() as LazyMap
return Response
}
Hope this helps
Looks like your server isn't fully HTTPBuilder-compilant. It should return 401 code (which is standart behaviour for REST servers, but non-standart for others) for HTTPBuilder to catch this status and resend authentication request. Here is written about it.

Resources