Using HTTPBuilder to execute a HTTP DELETE request - http

I'm trying to use the Groovy HTTPBuilder library to delete some data from Firebase via a HTTP DELETE request. If I use curl, the following works
curl -X DELETE https://my.firebase.io/users/bob.json?auth=my-secret
Using the RESTClient class from HTTPBuilder works if I use it like this:
def client = new RESTClient('https://my.firebase.io/users/bob.json?auth=my-secret')
def response = client.delete(requestContentType: ContentType.ANY)
However, when I tried breaking down the URL into it's constituent parts, it doesn't work
def client = new RESTClient('https://my.firebase.io')
def response = client.delete(
requestContentType: ContentType.ANY,
path: '/users/bob.json',
query: [auth: 'my-secret']
)
I also tried using the HTTPBuilder class instead of RESTClient
def http = new HTTPBuilder('https://my.firebase.io')
// perform a POST request, expecting TEXT response
http.request(Method.DELETE, ContentType.ANY) {
uri.path = '/users/bob.json'
uri.query = [auth: 'my-secret']
// response handler for a success response code
response.success = { resp, reader ->
println "response status: ${resp.statusLine}"
}
}
But this also didn't work. Surely there's a more elegant approach than stuffing everything into a single string?

There's an example of using HttpURLClient in the tests to do a delete, which in its simplest form looks like:
def http = new HttpURLClient(url:'https://some/path/')
resp = http.request(method:DELETE, contentType:JSON, path: "destroy/somewhere.json")
def json = resp.data
assert json.id != null
assert resp.statusLine.statusCode == 200
Your example is very close to the test for the delete in a HTTPBuilder.
A few differences I see are:
Your path is absolute and not relative
Your http url path doesn't end with trailing slash
You're using content type ANY where test uses JSON. Does the target need the content type to be correct? (Probably not as you're not setting it in curl example unless it's doing some voodoo on your behalf)
Alternatively you could use apache's HttpDelete but requires more boiler plate. For a HTTP connection this is some code I've got that works. You'll have to fix it for HTTPS though.
def createClient() {
HttpParams params = new BasicHttpParams()
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1)
HttpProtocolParams.setContentCharset(params, "UTF-8")
params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true)
SchemeRegistry registry = new SchemeRegistry()
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80))
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry)
HttpConnectionParams.setConnectionTimeout(params, 8000)
HttpConnectionParams.setSoTimeout(params, 5400000)
HttpClient client = new DefaultHttpClient(ccm, params)
return client
}
HttpClient client = createClient()
def url = new URL("http", host, Integer.parseInt(port), "/dyn/admin/nucleus$component/")
HttpDelete delete = new HttpDelete(url.toURI())
// if you have any basic auth, you can plug it in here
def auth="USER:PASS"
delete.setHeader("Authorization", "Basic ${auth.getBytes().encodeBase64().toString()}")
// convert a data map to NVPs
def data = [:]
List<NameValuePair> nvps = new ArrayList<NameValuePair>(data.size())
data.each { name, value ->
nvps.add(new BasicNameValuePair(name, value))
}
delete.setEntity(new UrlEncodedFormEntity(nvps))
HttpResponse response = client.execute(delete)
def status = response.statusLine.statusCode
def content = response.entity.content
I adopted the code above from a POST version, but the principle is the same.

Related

Sending a Post Request from Ballerina

I want to send a post request using ballerina to get a access token from the Choreo Dev Portal. I am able to do it using postman. But unable to make it work in Ballerina code level. it gives 415 - unsupported media type error. Need some Help in Ballerina
import ballerina/http;
import ballerina/io;
import ballerina/url;
public function main() returns error? {
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
http:Request request = new();
string payload = string`grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=*******&
subject_token_type=urn:ietf:params:oauth:token-type:jwt&
requested_token_type=urn:ietf:params:oauth:token-type:jwt`;
string encodedPayload = check url:encode(payload, "UTF-8");
io:print(encodedPayload);
request.setTextPayload(encodedPayload);
request.addHeader("Authorization","Basic *****");
request.addHeader("Content-Type","application/x-www-form-urlencoded");
io:print(request.getTextPayload());
json resp = check clientEndpoint->post("/oauth2/token",request);
io:println(resp.toJsonString());
}
I was expecting an access token from Choreo Devportal for the particular application.
import ballerina/http;
import ballerina/io;
import ballerina/mime;
public function main() returns error? {
// Creates a new client with the backend URL.
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
json response = check clientEndpoint->post("/oauth2/token",
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"requested_token_type":"urn:ietf:params:oauth:token-type:jwt",
"subject_token":"****"
},
{
"Authorization": "Basic ****"
},
mime:APPLICATION_FORM_URLENCODED
);
io:println(response.toString());
}
This is the recommended way to send the post request with the form URL encoded payload.
Change the Content-type header setting method from addHeader() to setHeader().
The request.setTextPayload(encodedPayload); will set the Content-type as text/plain as the default content type header.
Later request.addHeader("Content-Type","application/x-www-form-urlencoded"); is executed. The addHeader() method will append the new value to the same header in addition to the previously added text/plain. But the setHeader() will replace the previously set header which is the correct way in this scenario.
However better way is to pass the Content-type as the second param of setXXXPayload() method.
request.setTextPayload(encodedPayload, "application/x-www-form-urlencoded");

FastAPI file upload

I am trying to upload JSON data + file (binary) to FastAPI 'POST' endpoint using requests.
This is the server code:
#app.post("/files/")
async def create_file(
file: bytes = File(...), fileb: UploadFile = File(...), timestamp: str = Form(...)
):
return {
"file_size": len(file),
"timestamp": timestamp,
"fileb_content_type": fileb.content_type,
}
This is the client code:
session = requests.Session()
adapter = requests.adapters.HTTPAdapter(max_retries=0)
session.mount('http://', adapter)
jpg_image = open(IMG_PATH, 'rb').read()
timestamp_str = datetime.datetime.now().isoformat()
files = {
'timestamp': (None, timestamp_str),
'file': ('image.jpg', jpg_image),
}
request = requests.Request('POST',
FILE_UPLOAD_ENDPOINT,
files=files)
prepared_request = request.prepare()
response = session.send(prepared_request)
The server fails with
"POST /files/ HTTP/1.1" 422 Unprocessable Entity
FastAPI endpoints usually respond 422 when the request body is missing a required field, or there are non-expected fields, etc.
It seems that you are missing the fileb from your request body.
If this field is optional, you must declare it as follows in the endpoint definition:
fileb: Optional[UploadFile] = File(None)
You will also need to make some checks inside your endpoint code...
If it is a required field then you need to add it to your request body.

How to add header in Rest aussured pf RestTemplate

I have been trying to automate some API Testing using either resttemplate or Restassured library but im facing issues on post request. I cant seems to figure how to handle this. I keep getting 415 unsupported type error , I tried many ideas already and read hundreds of threads. Please if someone have a solution let me know.
This is the developer code
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
public Response postData(#FormDataParam("file") InputStream is,
#FormDataParam("file") FormDataContentDisposition fileName,
#FormDataParam("checkInCommInfoInput") String checkInCommInfoInput,
#HeaderParam("authorization") String authString) { }
This is what I tried with resttemplate
String addURI = "https://myURI";
HttpHeaders headers = new HttpHeaders();
//headers.add("Accept","*/*");
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Authorization", " a values will be here ");
System.out.println("**************************"+headers.getContentType());
String jsonBody = "my json file will be here";
//System.out.println("\n\n" + jsonBody);
HttpEntity<String> entity = new HttpEntity<String>(jsonBody, headers);
//POST Method to Add New Employee
response = this.restTemplate.postForEntity(addURI, entity, String.class);
responseBodyPOST = response.getBody();
// Write response to file
responseBody = response.getBody().toString();
What I tried with RestAssured
RestAssured.useRelaxedHTTPSValidation();
RestAssured.baseURI ="https://myURI";
EncoderConfig encoderconfig = new EncoderConfig();
Response response = RestAssured.given()
.header("Content-Type","application/json" )
.header("Authorization", "a vvalues will be here")
.header("Accept",ContentType.JSON )
.config(RestAssured.config()
.encoderConfig(encoderconfig.appendDefaultContentCharsetToContentTypeIfUndefined(false)))
//.contentType(ContentType.JSON)
// .accept("application/json")
.log().all().body(jsonBody).post()
.then()
.assertThat()
.log().ifError()
.statusCode(200)
.extract().response();
System.out.println("-------------"+ response.getBody().asString());
API under test #Consumes(MediaType.MULTIPART_FORM_DATA) but the test sends content with .header("Content-Type","application/json" )
415 error as you have clearly mentioned unsupported type error there is a content type mismatch between what the body content that the test client sends and what the API accepts.
See this blogpost: https://blog.jayway.com/2011/09/15/multipart-form-data-file-uploading-made-simple-with-rest-assured/ on how to do send MULTIPART_FORM_DATA

http4s Received premature EOF

I want to implement an http4s server that receives the content from another service, processes it and return the response.
The original service uses redirects so I added the Follow redirect middleware. I also added the Logger middleware to check the logs produced.
The skeleton of the service is:
implicit val clientResource = BlazeClientBuilder[F](global).resource
val wikidataEntityUrl = "http://www.wikidata.org/entity/Q"
def routes(implicit timer: Timer[F]): HttpRoutes[F] = HttpRoutes.of[F] {
case GET -> Root / "e" / entity => {
val uri = uri"http://www.wikidata.org/entity/" / ("Q" + entity)
val req: Request[F] = Request(uri = uri)
clientResource.use { c => {
val req: Request[F] = Request(Method.GET, uri)
def cb(resp: Response[F]): F[Response[F]] = Ok(resp.bodyAsText)
val redirectClient = Logger(true,true,_ => false)(FollowRedirect[F](10, _ => true)(c))
redirectClient.fetch[Response[F]](req)(cb)
}}}}
When I try to access the service with curl as:
curl -v http://localhost:8080/e/33
The response contains the first part of the original content and finnishes with:
transfer closed with outstanding read data remaining
* Closing connection 0
Looking at the logs, they content the following line:
ERROR o.h.s.blaze.Http1ServerStage$$anon$1 - Error writing body
org.http4s.InvalidBodyException: Received premature EOF.
which suggests that there was an error receiving a premature EOF.
I found a possible answer in this issue: but the answers suggest to use deprecated methods like tohttpService.
I think I would need to rewrite the code using a streams, but I am not sure what's the more idiomatic way to do it. Some suggestions?
I received some help in the http4s gitter channel to use the toHttpApp method instead of the fetch method.
I was also suggested also to pass the client as a parameter.
The resulting code is:
case GET -> Root / "s" / entity => {
val uri = uri"http://www.wikidata.org/entity/" / ("Q" + entity)
val req: Request[F] = Request(Method.GET, uri)
val redirectClient = Logger(true,true,_ => false)(FollowRedirect[F](10, _ => true)(client))
redirectClient.toHttpApp.run(req)
}
and now it works as expected.
The toHttpApp method is intended for use in proxy servers.

How to add a request body to an IBM BPM 8.6 script task BPMRESTRequest object in order to invoke a REST api?

I am trying to invoke a REST api (POST operation) from IBM BPM 8.6, I have to use a script task, I am able to call the api no problem just that the api expects a request body with a json object in it and I have not found a way yet to add that to the request object which I create in the script. I am able to add headers and parameters but not a http body to the request.
This is my code in the script tab of the the script task, the call is received by the api but it discards it with the message that the request is missing a request body which the api expects.
var request = new BPMRESTRequest();
request.externalServiceName = "api-docs";
request.operationName="extractReporterInfoUsingPOST";
request.httpHeaders = {"Content-Type": "application/json", "Accept":
"application/json"};
request.httpMethod = "POST";
// request.body = {"test":"dummy"}; <- does not work
// request.httpBody = {"test":"dummy"}; <- does not work
var response = tw.system.invokeREST(request);
I had the same problem. I was able to solve this issue by specifying the post body like so:
request.parameters = { "body": { "key1": "val1", "key2", "val2" }}
In parameters object add the key as the name of the body you gave or mentioned in swagger files request.parameters = { "nameOfBodyAsMenitionedInSwagger": { "key1": "val1", "key2", "val2" }}

Resources