HTTPBuilder - Cannot invoke method withInputStream() on null object - groovy

I have a groovy script using HTTPBuilder to make a RestAPI call to an SSL site. To connect I have imported the certs into a new keystore and use the recommended code in the HTTPBuilder support pages https://github.com/jgritman/httpbuilder/wiki/SSL
Here is the code:
def url = 'https://1.1.1.1'
def uri = new URIBuilder(url)
uri.path = '/rest/com/session'
def httpLOGIN = new HTTPBuilder(uri)
def keyStore = KeyStore.getInstance(KeyStore.defaultType)
getClass().getResource("\keystore.jks").withInputStream {
keyStore.load( it, "password".toCharArray())
}
String TempBasicAuth = "username:password".bytes.encodeBase64().toString()
httpLOGIN.client.connectionManager.schemeRegistry.register(
new Scheme("https", 443, new SSLSocketFactory(keyStore))
)
httpLOGIN.request(POST,JSON) { req ->
headers.'Authorization' = "Basic $TempBasicAuth"
response.failure = { resp, json ->
println "POST Failure. LOGIN: ${resp.statusLine}"
}
response.success = {// resp, json ->
println "POST Success. LOGIN: ${resp.statusLine}"*/
}
}
The response I get is
Caught: java.lang.NullPointerException: Cannot invoke method withInputStream() on null object
java.lang.NullPointerException: Cannot invoke method withInputStream() on null object
I've tried providing the full path to the keystore but get the same result. Is it that I have not supplied the path correctly (I've tried multiple iterations) or is there another issue?

The issue was explained in this question -
Loading resources using getClass().getResource()
getResource is looking for the path relative to the class / script path and not the file path.

Related

Passing token from get request to a header in SoapUI

I have a getToken request in a test case get_Admin_Token in PassToken test suite, where I have as a response following JSON:
{
"access_token": "5701f536-0bd5-441f-a490-21aafeasdasdd",
"token_type": "bearer",
"refresh_token": "c53af657-8292-4aff-xxxx-xxxf0ffed310",
"expires_in": 80208,
"scope": "read write trust"
}
I need to use access_token value in uploadFile method, but I need to pass it in a header. I have a field Authorization with Bearer: $(access_token) value.
Using some google I found:
https://community.smartbear.com/t5/SoapUI-Open-Source/How-do-I-do-a-property-transfer-with-multiple-source-responses/td-p/106456 question, which was looking similar. I started to create a GroovyScript test step, where I used code to pass it to the Properties table, but no success. I was also trying to put it to assertions for the get_Admin_Token, but I got a message about incorrect object types. I also tried to use def accessToken = jsonSlurper.access_token.toString() to use strings, but now I got an error `
No signature of method:
com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase.setProperty() is
applicable for argument types: (java.lang.String, java.lang.String) values:
[AUTH_KEY, Bearer 5701f536-0bd5-441f-a490-21aafeasdasdd] Possible solutions:
getProperty(java.lang.String), addProperty(java.lang.String),
hasProperty(java.lang.String), hasProperty(java.lang.String), getProject(),
getProperties()
My groovy code:
import groovy.json.JsonSlurper
def response = messageExchange.response.responseContent
def jsonSlurper = new JsonSlurper().parseText(response)
assert !(jsonSlurper.isEmpty())
def accessToken = jsonSlurper.access_token.toString()
assert null != accessToken, "access_token does not have a value"
def authorizationKey = "${accessToken}"
context.testCase.setProperty('AUTH_KEY',"Bearer " + authorizationKey)
Is this code valid? I'm not sure what to put in next method as authorization value in header, I tried with ${#get_Admin_Token#AUTH_KEY}, but it doesn't work
EDIT: Easier way
Just pass Token to properties using transfer action and set in header Bearer ${Properties#AdminToken}. That's all
===================
Old version:
The following answer is correct if anyone is looking for the Groovy script:
Ok, I think I spotted a workaround.
Groovy code is as following:
import groovy.json.JsonSlurper
def response = messageExchange.response.responseContent
def jsonSlurper = new JsonSlurper().parseText(response)
assert !(jsonSlurper.isEmpty())
def accessToken = jsonSlurper.access_token.toString()
assert null != accessToken, "access_token does not have a value"
def authorizationKey = "${accessToken}"
context.testCase.testSuite.setPropertyValue("AUTH_KEY","Bearer " + authorizationKey)
log.info context.testCase.testSuite.getPropertyValue( "AUTH_KEY" )
And using answer presented here: How to transfer dynamic auth value in all requests instead of changing the value in every request's header in SOAPUI I created a new GroovyScript Test Case:
testRunner.testCase.testSteps.each{ name, testStep ->
log.info name
if(testStep.metaClass.getMetaMethod("getTestRequest")){
if(name=="UploadScreenshot"){
def request = testStep.getTestRequest()
def headers = request.getRequestHeaders()
headers.add('Authoritzation',context.testCase.testSuite.getPropertyValue( "AUTH_KEY" ))
request.setRequestHeaders(headers)
log.info "Added header to $name"
}
}
}
I know it's not a very good idea, to put an if to the loop instead if delete a loop, but I don't know yet how to do it and I need to proceed with work

How to call REST from jenkins workflow

I wonder how to call REST API from a (groovy) Jenkins workflow script. I can execute "sh 'curl -X POST ...'" - it works, but building the request as a curl command is cumbersome and processing the response gets complicated. I'd prefer a native Groovy HTTP Client to program in groovy - which one should I start with? As the script is run in Jenkins, there is the step of copying all needed dependency jars to the groovy installation on Jenkins, so something light-weight would be appreciated.
Native Groovy Code without importing any packages:
// GET
def get = new URL("https://httpbin.org/get").openConnection();
def getRC = get.getResponseCode();
println(getRC);
if(getRC.equals(200)) {
println(get.getInputStream().getText());
}
// POST
def post = new URL("https://httpbin.org/post").openConnection();
def message = '{"message":"this is a message"}'
post.setRequestMethod("POST")
post.setDoOutput(true)
post.setRequestProperty("Content-Type", "application/json")
post.getOutputStream().write(message.getBytes("UTF-8"));
def postRC = post.getResponseCode();
println(postRC);
if(postRC.equals(200)) {
println(post.getInputStream().getText());
}
There is a built in step available that is using Jenkins HTTP Request Plugin to make http requests.
Plugin: https://wiki.jenkins-ci.org/display/JENKINS/HTTP+Request+Plugin
Step documentation: https://jenkins.io/doc/pipeline/steps/http_request/#httprequest-perform-an-http-request-and-return-a-response-object
Example from the plugin github page:
def response = httpRequest "http://httpbin.org/response-headers?param1=${param1}"
println('Status: '+response.status)
println('Response: '+response.content)
I had trouble installing the HTTPBuilder library, so I ended up using the more basic URL class to create an HttpUrlConnection.
HttpResponse doGetHttpRequest(String requestUrl){
URL url = new URL(requestUrl);
HttpURLConnection connection = url.openConnection();
connection.setRequestMethod("GET");
//get the request
connection.connect();
//parse the response
HttpResponse resp = new HttpResponse(connection);
if(resp.isFailure()){
error("\nGET from URL: $requestUrl\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
}
this.printDebug("Request (GET):\n URL: $requestUrl");
this.printDebug("Response:\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
return resp;
}
/**
* Posts the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
*/
HttpResponse doPostHttpRequestWithJson(String json, String requestUrl){
return doHttpRequestWithJson(json, requestUrl, "POST");
}
/**
* Posts the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
*/
HttpResponse doPutHttpRequestWithJson(String json, String requestUrl){
return doHttpRequestWithJson(json, requestUrl, "PUT");
}
/**
* Post/Put the json content to the given url and ensures a 200 or 201 status on the response.
* If a negative status is returned, an error will be raised and the pipeline will fail.
* verb - PUT or POST
*/
HttpResponse doHttpRequestWithJson(String json, String requestUrl, String verb){
URL url = new URL(requestUrl);
HttpURLConnection connection = url.openConnection();
connection.setRequestMethod(verb);
connection.setRequestProperty("Content-Type", "application/json");
connection.doOutput = true;
//write the payload to the body of the request
def writer = new OutputStreamWriter(connection.outputStream);
writer.write(json);
writer.flush();
writer.close();
//post the request
connection.connect();
//parse the response
HttpResponse resp = new HttpResponse(connection);
if(resp.isFailure()){
error("\n$verb to URL: $requestUrl\n JSON: $json\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
}
this.printDebug("Request ($verb):\n URL: $requestUrl\n JSON: $json");
this.printDebug("Response:\n HTTP Status: $resp.statusCode\n Message: $resp.message\n Response Body: $resp.body");
return resp;
}
class HttpResponse {
String body;
String message;
Integer statusCode;
boolean failure = false;
public HttpResponse(HttpURLConnection connection){
this.statusCode = connection.responseCode;
this.message = connection.responseMessage;
if(statusCode == 200 || statusCode == 201){
this.body = connection.content.text;//this would fail the pipeline if there was a 400
}else{
this.failure = true;
this.body = connection.getErrorStream().text;
}
connection = null; //set connection to null for good measure, since we are done with it
}
}
And then I can do a GET with something like:
HttpResponse resp = doGetHttpRequest("http://some.url");
And a PUT with JSON data using something like:
HttpResponse resp = this.doPutHttpRequestWithJson("{\"propA\":\"foo\"}", "http://some.url");
Have you tried Groovy's HTTPBuilder Class?
For example:
#Grapes(
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
)
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
def http = new HTTPBuilder("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo")
http.request(POST, JSON ) { req ->
body = []
response.success = { resp, reader ->
println "$resp.statusLine Respond rec"
}
}
Blocking the main thread on I/O calls is not a good idea.
Delegating the I/O operation to a shell step is the recommended way currently.
The other way, which requires development, is to add a new step. By the way, there is an initiative to add a common set of steps to be used securely inside the pipeline script, although a full REST client owes its own plugin.
Do a GET with the Basic Auth header.
def accessToken = "ACCESS_TOKEN".bytes.encodeBase64().toString()
def req = new URL("https://raw.githubusercontent.com/xxxx/something/hosts").openConnection();
req.setRequestProperty("Authorization", "Basic " + accessToken)
def content = req.getInputStream().getText()

Response from a POST request in Groovy RESTClient is missing data

I am using groovy RESTClient 0.6 to make a POST request. I expect an XML payload in the response. I have the following code:
def restclient = new RESTClient('<some URL>')
def headers= ["Content-Type": "application/xml"]
def body= getClass().getResource("/new_resource.xml").text
/*
If I omit the closure from the following line of code
RESTClient blows up with an NPE..BUG?
*/
def response = restclient.post(
path:'/myresource', headers:headers, body:body){it}
println response.status //prints correct response code
println response.headers['Content-Length']//prints 225
println response.data //Always null?!
The response.data is always null, even though when I try the same request using Google chrome's postman client, I get back the expected response body. Is this a known issue with RESTClient?
The HTTP Builder documentation says that data is supposed to contain the parsed response content but, as you've discovered, it just doesn't. You can, however, get the parsed response content from the reader object. The easiest, most consistent way I've found of doing this is to set the default success and failure closures on your RESTClient object like so:
def restClient = new RESTClient()
restClient.handler.failure = { resp, reader ->
[response:resp, reader:reader]
}
restClient.handler.success = { resp, reader ->
[response:resp, reader:reader]
}
You'll get the same thing on success and failure: a Map containing the response (which is an instance of HttpResponseDecorator) and the reader (the type of which will be determined by the content of the response body).
You can then access the response and reader thusly:
def map = restClient.get([:]) // or POST, OPTIONS, etc.
def response = map['response']
def reader = map['reader']
assert response.status == 200
I faced a similar issue and I took the cue from Sams solution but used closures to address it (similar solution but coded using closures instead of the returned object).
resp.data is always null when using the RESTClient, however the reader contains the data, so it would look something like this:
def restclient = new RESTClient('<some URL>')
def headers= ["Content-Type": "application/xml"]
def body= getClass().getResource("/new_resource.xml").text
try {
restclient.post(path:'/myresource', headers:headers, body:body) { resp, reader ->
resp.status // Status Integer
resp.contentType // Content type String
resp.headers // Map of headers
resp.data // <-- ALWAYS null (the bug you faced)
reader // <-- Data you're looking for
}
} catch (Exception e) {
e.response.status // Get HTTP error status Integer
}

HTTPBuilder gets 403 while cURL works fine (GitHub API)

A GET request sent to https://api.github.com/users/username works from the command line and via URL#text, but fails using HTTPBuilder.
The code:
new HTTPBuilder('https://api.github.com').get(path: '/users/xan', contentType: JSON) // fails
"https://api.github.com/users/xan".toURL().text // works
On the command line:
# works:
$ curl https://api.github.com/users/xan
Also a spock test is available in this gist
Why?
Eventually I found out: GitHub denies access if the User-Agent header is missing.
This works:
def http = new HTTPBuilder('https://api.github.com')
def response = http.get(path: '/users/qmetric',
headers: [(USER_AGENT): "Apache HTTPClient"])
Because you must accept the good conten type aswell.
i.e.
def http = new HTTPBuilder('http://ajax.googleapis.com')
http.request( Method.GET, ContentType.TEXT ) { req ->
uri.path = '/ajax/services/search/web'
uri.query = [ v:'1.0', q: 'Calvin and Hobbes' ]
headers.Accept = 'application/json'
response.success = { resp, reader ->
println "Got response: ${resp.statusLine}"
println "Content-Type: ${resp.headers.'Content-Type'}"
print reader.text
}
}

XmlSlurper.parse(uri) with HTTP basic authentication

I need to grab a data from XML-RPC web-service.
new XmlSlurper().parse("http://host/service") works fine, but now I have a particular service that requires basic HTTP authentication.
How can I set username and password for parse() method, or modify HTTP headers of the request?
Using http://username:password#host/service doesn't help - I still get java.io.IOException: Server returned HTTP response code: 401 for URL exception.
Thanks
I found this code over here which might help?
Editing this code to your situation, we get:
def addr = "http://host/service"
def authString = "username:password".getBytes().encodeBase64().toString()
def conn = addr.toURL().openConnection()
conn.setRequestProperty( "Authorization", "Basic ${authString}" )
if( conn.responseCode == 200 ) {
def feed = new XmlSlurper().parseText( conn.content.text )
// Work with the xml document
} else {
println "Something bad happened."
println "${conn.responseCode}: ${conn.responseMessage}"
}
This will work for you
Please remember to use this instead of the 'def authString' mentioned above:
def authString = "${usr}:${pwd}".getBytes().encodeBase64().toString()

Resources