scala.js.dom ajax.post, error when error status - scala.js

I a using scala.js (0.6.5) and scala-js-dom (0.8.2) and I have some strange pb with an ajax.post, when I receive an error status (409 here).
The browser console shows an error message, but from my scala code I cannot have access to the status code, and to the message returned.
Here is the code I use for sending a POST:
val request = Ajax.post(
url,
data = postData,
headers = bsHeaders)
request.map(xhr => {
log.debug("response text: " + xhr.responseText)
if (xhr.status == 201) {
try {
val loc = xhr.getResponseHeader("Location")
if(loc == locHeaderResp) {
loc
} else {
log.error(s"Location header invalid: ${loc}")
}
} catch {
case e:Exception => {
log.error("Couldn't read 'Location' header " + e.getMessage)
log.debug("List of headers: " + xhr.getAllResponseHeaders())
""
}
}
} else if (xhr.status == 409) {
log.error("" + xhr.responseText)
log.error(s"${xhr.responseText}")
} else {
log.error(s"Request failed with response code ${xhr.status}")
log.error(s"${xhr.responseText}")
}
})
When the status is 201, it works well.
In my case, when the data I am sending already exists, I am supposed to get a 409 error code, with some message status. And from the browser debugging tools it is indeed the case.
I was expecting to be able to manage error case when doing the 'request.map', but when an error code is returned, this code is not executed.
So how to manage errors with POST messages?

This is expected. Ajax.post returns a Future, and the map method of Futures are only executed for the successful cases. A return code of 409 is considered a failure, and will therefore complete the future with a failed status.
To handle failures with Futures, you should use their onFailure method:
request.map(req => {
// ... handle success cases (req.status is 2xx or 304)
}).onFailure {
case dom.ext.AjaxException(req) =>
// ... handle failure cases (other return codes)
})
If you would rather deal with failure return codes in the same code as success return codes, you can first recover to turn a failed AjaxException(req) into a successful req:
request.recover {
// Recover from a failed error code into a successful future
case dom.ext.AjaxException(req) => req
}.map(req => {
// handle all status codes
}

Related

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

akka http handleNotFound rejection is only working for POST method

i have the following akka http rejection handling code taken from https://doc.akka.io/docs/akka-http/current/routing-dsl/rejections.html
val message = "The requested resource could not be found."
implicit def myRejectionHandler = RejectionHandler.newBuilder()
.handleNotFound {
complete(HttpResponse(NotFound
,entity = HttpEntity(ContentTypes.`application/json`, s"""{"rejection": "$message"}"""
)))
}.result()
val route: Route = handleRejections(myRejectionHandler) {
handleExceptions(myExceptionHandler) {
concat(
path("event-by-id") {
get {
parameters('id.as[String]) {
id =>
complete("id")
}
}
}
,
post {
path("create-event") {
entity(as[Event]) {
event =>
complete(OK, "inserted")
}
}
}
)
}
}
}
val bindingFuture = Http().bindAndHandle(route, hostName, port)
when i hit localhost:8080/random
i got the message
HTTP method not allowed, supported methods: POST
and when i select POST and hit localhost:8080/random
i got the message
{
"rejection": "The requested resource could not be found."
}
why i did not get the same message when my route request was GET ?
in the docs the handleNotFound was working with GET request https://doc.akka.io/docs/akka-http/current/routing-dsl/rejections.html
This is happens, probably because of order of directives, you are using: in your configuration if incoming request does not match with event-by-id URL path, then it goes to the next handler, which expects that request should have POST method first of all, because post directive goes first, before path("create-event").
What you can try to do is change directives order to the next one, for second route:
path("create-event") {
post {
entity(as[Event]) { event =>
complete(OK, "inserted")
}
}
}
Hope this helps!

Server Sent Events with Kitura

I try to do a client-server application with on the client side a Angular2/typescript web site and on the server side a Kitura server in Swift on Mac OSX.
On the client side, the typescript code instanciates an EventSource object :
this.eventSource = new EventSource(this.webSocketServerUrl);
this.eventSource.onopen = (event: Event): any => {
console.log("ServerNotificationsService.onopen - " + JSON.stringify(event) + " " + this.eventSource.readyState);
event.stopPropagation();
return null;
}
this.eventSource.onerror = (event: sse.IOnMessageEvent) => {
console.log("ServerNotificationsService.onerror - " + JSON.stringify(event) + " " + this.eventSource.readyState);
}
this.eventSource.onmessage = (event: Event): any => {
console.log("ServerNotificationsService.onmessage - " + JSON.stringify(event) + " " + this.eventSource.readyState);
event.stopPropagation();
return null;
}
console.log("ServerNotificationsService.constructor - " + this.eventSource.readyState);
On the server side, my code to handle the GET request looks like this :
router.get("/notifications") { request, response, next in
response.headers.append("Access-Control-Allow-Origin", value: "*")
if((request.accepts(header: "Accept", type: "text/event-stream")) != nil)
{
response.headers.append("content-type", value: "text/event-stream; charset=utf-8")
response.headers.append("cache-control", value: "no-cache")
response.headers.append("connection", value: "keep-alive")
try response.end()
Logger.sharedInstance.verbose(msg: "Request GET /notifications OK")
}
else
{
try response.status(.internalServerError).end()
Logger.sharedInstance.verbose(msg: "Request GET /notifications internalServerError")
}
next()
}
and to handle the post request :
router.post("/notifications") { request, response, next in
Logger.sharedInstance.verbose(msg: "Request POST /notifications ...")
response.headers.append("Access-Control-Allow-Origin", value: "*")
response.headers.append("content-type", value: "text/event-stream; charset=utf-8")
response.headers.append("cache-control", value: "no-cache")
response.headers.append("connection", value: "keep-alive")
while (true)
{
// wait here 5s. for the <nextMessage>
response.send(<nextMessage>)
try response.end()
Logger.sharedInstance.verbose(msg: "Request POST /notifications OK")
next()
}
}
The problem is that on the client side I receive the onopen notification, the event source's readyState pass to "Open" (1) but I receive immediately after a onerror notification and the readyState pass to "Connecting" (0). And so on : connecting, close, connecting, close, ... And in consequence the post request is never executed.
I will appreciate some help to have a code that maintains an open connexion.
Thank you,
Notux
Kitura does not currently support persistent, open HTTP connections. However, you might be able to replicate the functionality using WebSocket instead of server-sent events over HTTP (you will need to rewrite your frontend code to use WebSockets instead of EventSource):
https://github.com/IBM-Swift/Kitura-WebSocket
And example Kitura-WebSocket app can be found here:
https://github.com/IBM-Swift/Kitura-Chat-Server

SOAP Request to Wemo switch from Pebble returns status 500

Been trying to write a pebble app for wemo switches, currently this is the code i'm using:
function WemoRequest(callback) {
if (SOAPData === false || SOAPData === undefined) {
console.log("Invalid SOAP data: " + JSON.stringify(SOAPData));
return;
}
var url = "http://192.168.1.230:49153/upnp/control/basicevent1";
try {
var request = new XMLHttpRequest();
request.open("POST", url, false);
request.setRequestHeader("SOAPAction", "urn:Belkin:service:basicevent:1#GetBinaryState");
request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status === 200 && callback) {
callback(request, SOAPData);
}else{console.log("Status: "+request.status + " State: "+request.readyState+" Callback: "+callback);}
};
var packet = '<?xml version="1.0" encoding="utf-8"?>'+
'<s:Envelope xmls:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'+
'<s:Body>'+
'<u:GetBinaryState xmlns:u="urn:Belkin:service:basicevent:1"></u:GetBinaryState>'+
'</s:Body>'+
'</s:Envelope>';
request.send(packet);
} catch (error) {
console.log("Error in XMLHttpRequest: " + error);
}}
I currently get status 500 from OnReadyStateChange and have no idea what I'm doing wrong. If this isn't enough code, app code is available here:https://github.com/dmf444/Webble
So...I know this is from 4 years ago lol, but I found this during a google search and just found the answer, so I figured I would respond for that reason: I think your header just needs an extra set of quotes around "urn:Belkin:service:basicevent:1#SetBinaryState" so that the string specifying the soapaction literally starts and ends with quotes.
I'm working in Python (because that's what all the kids seem to be doing these days), but I too was getting the 500 error until I made a very subtle change (the single quote marks around my double quotes) and almost cried tears of joy when my light turned off:
"SOAPACTION": '"urn:Belkin:service:basicevent:1#SetBinaryState"'
So here's the working version of the code (in Python lol):
import http.client
#Variables (value=on/off, ipaddress=address of your wemo)
value = 0 #1=ON, 0=OFF
ipAddress = "192.168.0.108"
#Build the SOAP Envelope (data)
data = '<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1"><BinaryState>' + str(value) + '</BinaryState></u:SetBinaryState></s:Body></s:Envelope>'
#Build the Header (headers)
headers = {"Content-type" : 'text/xml; charset="utf-8"', "SOAPACTION": '"urn:Belkin:service:basicevent:1#SetBinaryState"', "Content-Length": len(data)}
#Send request and check response data (resp_data)
conn = http.client.HTTPConnection(ipAddress, 49153)
conn.request("POST", "/upnp/control/basicevent1", data, headers)
response = conn.getresponse()
resp_data = response.read()
if response.status == 200:
conn.close()
print("SUCCESS!")
elif response.status == 403:
print("ERROR: 403 (FORBIDDEN)")
else:
print("ERROR: " + str(response.status))

Unable to run a POST action in play framework 2.2

I have a similar problem to this person and I am unsure why.
my post action is called from a form submission and the controller code
def processFreeDoc = UserAwareAction {
implicit request => {
userEmailForm.bindFromRequest.fold(
formWithErrors => {
Logger.error("Error processing the form %s "format formWithErrors.errors)
Redirect(routes.Application.index)
},
data => {
val sessionProduct = getSessionUnProcessedProduct(data.sessionId, data.documentName)
if(sessionProduct != null){
Logger.info("Found")
sessionProduct.updateProcessing(data.emailAddress, data.sessionId)
Redirect(routes.Application.index)
}
else
Logger.info("Nothing found")
Redirect(routes.Application.checkoutFree(data.sessionId))
}
)
}
}
is skipped entirely. There are no errors in the IDE(IDEA) console and the breakpoint at the entry of the method is not reached so none of the log messages are seen.
EDIT :
The relevant route in the routes file - POST /processFreeDoc controllers.Application.processFreeDoc