Databinder dispatch: Get uncompressed content of a 403 response - scala

I am using databinder dispatch for making HTTP requests which works nicely, as long as the web server returns a 404.
If the request fails, the web server returns a 403 status code and provides a detailed error message in the response body as XML.
How to read the xml body (regardless of the 403), e.g. how can I make dispatch ignore all 403 errors?
My code looks like this:
class HttpApiService(val apiAccount:ApiAccount) extends ApiService {
val http = new Http
override def baseUrl() = "http://ws.audioscrobbler.com/2.0"
def service(call:Call) : Response = {
val http = new Http
var req = url(baseUrl())
var params = call.getParameterMap(apiAccount)
var response: NodeSeq = Text("")
var request: Request = constructRequest(call, req, params)
// Here a StatusCode exception is thrown.
// Cannot use StatusCode case matching because of GZIP compression
http(request <> {response = _})
//returns the parsed xml response as NodeSeq
Response(response)
}
private def constructRequest(call: Call, req: Request, params: Map[String, String]): Request = {
val request: Request = call match {
case authCall: AuthenticatedCall =>
if (authCall.isWriteRequest) req <<< params else req <<? params
case _ => req <<? params
}
//Enable gzip compression
request.gzip
}
}

I believe something like this works:
val response: Either[String, xml.Elem] =
try {
Right(http(request <> { r => r }))
} catch {
case dispatch.StatusCode(403, contents) =>
Left(contents)
}
The error will be in Left. The success will be in Right. The error is a String that should contain the XML response you desire.
If you need more, I believe you can look at HttpExecutor.x, which should give you full control. It's been a while since I've used dispatch, though.
Also, I'd suggest using more val's and less var's.

Related

Is there a way to dynamically pass payload to my tests in pytest?

I want to the tests to get request body data dynamically
class TestPlaylistCreateView:
def test_create_playlist_with_valid_name(self, authorized_api_client):
url = reverse("create-playlist")
data = {
"name": name,
"is_public": "True"
} # <------- concrete payload
response = authorized_api_client.post(url, data)
assert response.status_code == status.HTTP_201_CREATED
def test_create_already_created_playlist(self, authorized_api_client, create_playlist):
url = reverse("create-playlist")
data = {
"name": create_playlist.name,
"is_public": create_playlist.is_public
} # <------- again the same payload
response = authorized_api_client.post(url, data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
I would like to avoid repeating myself everytime I need request body data. is there a way to do it, maybe through a fixture or something.
my fixtures for the test
#pytest.fixture
def create_user():
return UserFactory()
#pytest.fixture
def authorized_api_client(create_user):
api_client = APIClient()
api_client.force_authenticate(user=create_user)
return api_client
#pytest.fixture
def create_playlist():
return PlaylistFactory()

Making a route both intermediate and final?

In building an app with PerfectHTTP I've come up against a problem with routing: Routes that are used as intermediate routes cannot also be used as final routes.
When set up as below, some URLs track correctly (/index.html in the example), while some URLs do not (/ in the example).
Is there some special sauce I'm missing here, or is what I'm trying to do simply impossible using Perfect?
import PerfectHTTP
import PerfectHTTPServer
// This does various housekeeping to set things common to all requests.
var routes = Routes() {
request, response in
Log.info(message: "\(request.method) \(request.uri)")
response
.setHeader(.contentType, value: "text/plain")
.next()
}
// For this handler, http://localhost:8181/index.html works,
// but http://localhost:8181/ does not
routes.add(method: .get, uris: ["/", "index.html"]) {
request, response in
response
.appendBody(string: "Hello, world!")
.completed()
}
HTTPServer.launch(.server(name: "localhost", port: 8181, routes: [routes]))
if you want set things common to all requests. use filter.
my routes is working
see more https://perfect.org/docs/filters.html
var routes = Routes()
routes.add(method: .get, uris: ["/", "index.html"]) {
request, response in
response
.appendBody(string: "Hello, world!")
.completed()
}
struct MyFilter: HTTPRequestFilter {
func filter(request: HTTPRequest, response: HTTPResponse, callback: (HTTPRequestFilterResult) -> ()) {
print("request method: \(request.method)")
callback(.continue(request, response))
}
}
do {
let server = HTTPServer()
server.serverName = "testServer"
server.serverPort = 8181
server.addRoutes(routes)
server.setRequestFilters([(MyFilter(), HTTPFilterPriority.high)])
try server.start()
} catch {
fatalError("\(error)") // fatal error launching one of the servers
}

IllegalStateException thrown when reading the vert.x http client response body

I am getting the following error when I attempt to read the body from the http client response object. I don't get the exception all the time so I guess it's a threading issue related to the CompletableFuture. Any idea of what I am doing wrong? I use vert.x 3.8.1
java.lang.IllegalStateException
at io.vertx.core.http.impl.HttpClientResponseImpl.checkEnded(HttpClientResponseImpl.java:134)
at io.vertx.core.http.impl.HttpClientResponseImpl.endHandler(HttpClientResponseImpl.java:153)
at io.vertx.core.http.impl.HttpClientResponseImpl.bodyHandler(HttpClientResponseImpl.java:193)
at com.diagnostics.Response.body(Web.kt:116)
at com.diagnostics.Response.bodyNow(Web.kt:111)
at com.diagnostics.Response.bodyNow$default(Web.kt:110)
at com.diagnostics.Main.postVerificationTest(Main.kt:73)
at com.diagnostics.Main.main(Main.kt:52)
at com.diagnostics.Main.main(Main.kt)
Code that throws the exception:
val response = client.get(requestUri = "/api/info").get(3, TimeUnit.SECONDS)
val expectedStatus = 200
assertConditionOrExit(pvtLog, response.status == expectedStatus, "response status is ${response.status} expecting $expectedStatus")
val body = response.bodyNow()
assertConditionOrExit(pvtLog, body.isNotEmpty(), "body is empty expecting a non empty value")
The http client response object is created from the following code:
private fun request(
method: HttpMethod,
port: Int,
host: String,
requestUri: String
): CompletableFuture<Response> {
val future = CompletableFuture<Response>()
httpClient.request(method, port, host, requestUri)
.exceptionHandler { future.completeExceptionally(it) }
.handler { resp -> future.complete(Response(resp)) }
.end()
return future
}
And the body is retrieved...
fun bodyNow(timeout: Long = 10, unit: TimeUnit = SECONDS): String {
return body().get(30000, SECONDS)
}
fun body(): CompletableFuture<String> {
val future = CompletableFuture<String>()
resp.bodyHandler { buff -> future.complete(buff.toString())}
return future
}
The body() function sets a bodyHandler after the HttpClientRequest handler has been invoked in the request() method.
So there is a chance that, while your main thread proceeds, the eventloop receives content and drops it. If content is small, the request could even end before the bodyHandler is set.
This is why you only see the exception from time to time.
If you want to set the bodyHandler later, you must pause the HttpClientResponse:
httpClient.request(method, port, host, requestUri)
.exceptionHandler { future.completeExceptionally(it) }
.handler { resp ->
resp.pause() // Pause the response
future.complete(Response(resp))
}
.end()
Then resume it after setting the bodyHandler:
resp.bodyHandler { buff -> future.complete(buff.toString())}
resp.resume()

How to use return of one gatling request into another request - Scala

In the following code, I am getting a token in the first Gatling request, saving it in a variable named auth. However, when I try to use it in the second request, it is sending empty string in place of auth variable. So for some reason, the auth string is not being updated till the time it is being used in the second request. Can anyone suggest any workaround so that I can use the value returned in one request into another request?
Code:
val headers_10 = Map("Content-Type" -> "application/x-www-form-urlencoded")
var a= "qwerty91#gmail.com"
var auth = ""
val scn = scenario("Scenario Name") // A scenario is a chain of requests and pauses
.exec(http("request_1") // Here's an example of a POST request
.post("/token")
.headers(headers_10)
.formParam("email", a)
.formParam("password", "password")
.transformResponse { case response if response.isReceived =>
new ResponseWrapper(response) {
val a = response.body.string
auth = "Basic " + Base64.getEncoder.encodeToString((a.substring(10,a.length - 2) + ":" + "junk").getBytes(StandardCharsets.UTF_8))
}
})
.pause(2)
.exec(http("request_2")
.get("/user")
.header("Authorization",auth)
.transformResponse { case response if response.isReceived =>
new ResponseWrapper(response) {
val a = response.body.string
}
})
You should store the value you need in the session. Something like this will work, although you'll have to tweak the regex and maybe some other details:
val headers_10 = Map("Content-Type" -> "application/x-www-form-urlencoded")
var a= "qwerty91#gmail.com"
var auth = ""
val scn = scenario("Scenario Name") // A scenario is a chain of requests and pauses
.exec(http("request_1") // Here's an example of a POST request
.post("/token")
.headers(headers_10)
.formParam("email", a)
.formParam("password", "password")
.check(regex("token: (\\d+)").find.saveAs("auth")))
.pause(2)
.exec(http("request_2")
.get("/user")
.header("Authorization", "${auth}"))
Here's the documentation on "checks", which you can use to capture values from a response:
http://gatling.io/docs/2.2.2/http/http_check.html
Here is the documentation on the gatling EL, which is the easiest way to use session variables (this is the "${auth}" syntax in the last line above):
http://gatling.io/docs/2.2.2/session/expression_el.html

Playframework WS API response processing

I use The Play WS API from PlayFramework to communicate with external API.
I need to process the received data, but don't know how. I get a response, and I want to pass it to other function like an JSON Object. How I can achieve that?
Code I use you can see below.
Thanks!
def getTasks = Action {
Async {
val promise = WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get()
for {
response <- promise
} yield Ok((response.json \\ "Tasks"))
}
}
I get a response, and I want to pass it to other function like an JSON Object.
I'm not sure I understand your question, but I'm guessing you want to transform the json you receive from the WS call prior to returning to the client, and that this transformation could take several lines of code. If this is correct, then you just need to add curly brackets around your yield statement so you can do more work on the response:
def getTasks = Action {
Async {
val promise = WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get()
for {
response <- promise
} yield {
// here you can have as many lines of code as you want,
// only the result of the last line is yielded
val transformed = someTransformation(response.json)
Ok(transformed)
}
}
}
I took a look at the doc, and you could try:
Async {
WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get().map{
response => Ok(response.json \\ "Tasks")
}
}