Is it possible to use websockets to send a request and get a response right after (without the publisher/subscriber structure)? - kotlin

I am using spring-websocket together with Kotlin Multiplatform, SockJS and StompJS.
I've got my application up and running, mainly using this tutorial from Spring. The frontend is able to send messages to the backend (via the #MessageMapping annotation), which responds back to frontend (via the #SendTo annotation).
The frontend code currently looks like this:
val socket = SockJS("http://localhost:8080/my-app")
val stompClient = Stomp.over(socket)
stompClient.connect(json(), { frame ->
stompClient.subscribe("/topic/a-topic", { message ->
// message handling code
})
})
stompClient.send("/app/hello", null, "")
The subscription approach is useful when an action from some user needs to affect all users (like sending a message in a group chat). However, there are times when some actions should affect only the user who started the action, and that is my problem.
Ideally, I would like to do something to the likes of this:
val socket = SockJS("http://localhost:8080/my-app")
val stompClient = Stomp.over(socket)
stompClient.connect(json(), {})
stompClient.send("/app/hello", null, "").then { response ->
// response handling code
}
But I don't know if it is possible.
I also thought about having a REST API handle this kind of request, but it doesn't seem optimal since there's the existing websocket connection already.
Any enlightment on the matter is appreciated.

Related

Kotlin for Volley, how can I check the JSON request for newer data in the API?

I'm working on an app that gets a list of documents/source URL from an api. I'd like to periodically check for new or updated contents within that API so users can update saved items in the database. I'm at a loss on the correct wording to search, thus Google and Stack Overflow have both failed me. My fetching function is below:
The URL for the API is https://api.afiexplorer.com
private fun fetchPubs() {
_binding.contentMain.loading.visibility = View.VISIBLE
request = JsonArrayRequest(
Request.Method.GET,
Config.BASE_URL,
JSONArray(),{ response ->
val items: List<Pubs> =
Gson().fromJson(response.toString(), object : TypeToken<List<Pubs>>() {}.type)
val sortedItems = items.sortedWith(compareBy { it.Number })
pubsList?.clear()
pubsList?.addAll(sortedItems)
// Hardcoded pubs moved to Publications Gitlab Repo
// https://gitlab.com/afi-explorer/pubs
_binding.contentMain.recyclerView.recycledViewPool.clear()
adapter?.notifyDataSetChanged()
_binding.contentMain.loading.visibility = View.GONE
setupData()
Log.i("LENGTH OF DATA", "${items.size}")
},
{error ->
println(error.printStackTrace())
Toasty.error(applicationContext, getString(string.no_internet), Toast.LENGTH_SHORT, true).show()
}
)
MyApplication.instance.addToRequestQueue(request!!)
}
private fun setupData(){
adapter = MainAdapter(applicationContext, pubsList!!, this)
_binding.contentMain.recyclerView.adapter = adapter
}
I tried using ChatGPT to see if that would get me started and that failed miserably. Also searched Google, Reddit and Stack Overflow for similar projects, but mine is a unique scenario I guess. I'm just a hobbyist and intermediate dev I guess. First time working with Volley, everything works, but I would like to find a way to send a notification (preferably not Firebase) if there is updated info within the API listed above. I'm not sure if this is actually doable.
Are you asking if you can somehow find if the remote API has changed its content? If so, how would that service advise you? If the service provider provides a web hook or similar callback you could write a server-based program to send a push notification to your Android app.
Perhaps you intent to poll the API periodically, and then you want to know if there is a change?
If you use a tool such as Postman or curl to easily see the headers of the API https://api.afiexplorer.com you will see, unfortunately, there is no Last-Modified header or ETag header which would allow you to easily determine if there was a change.
Next looking at the content of the API, the author does not provide an obvious version/change date, so no luck there.
What you could do is receive the content as a String, and perform a checksum operation on it, and if it differs you know there has been a change
or if you are deserialising the received JSON in Kotlin data classes, then out of the box, Kotlin will enable you to perform an equality operation on a previous copy of the data to know if there was a change.
This looks like an android app; if so, why don't you create a background service that makes requests to the API and updates the data as needed? You can use an AlarmManager class to set the interval threshold for polling by using the setInexactRepeating() method.
Most apps are updated in this fashion; sometimes, a separate table is created to catalog changesets.
Let me know if this helps.

What is the correct way to pipe a Ktor client response to a server response?

I'm trying to pipe a client response from the Ktor client library as a Ktor server response using the following code:
serviceClient.retrieveCourseZip(id, version).execute { response ->
call.respond(object : OutgoingContent.ReadChannelContent() {
override val contentType = response.contentType()
override val contentLength = response.contentLength()
override val status = response.status
override val headers = headersOf(
HttpHeaders.ContentDisposition to listOf("attachment; filename=\"course-$courseId.zip\"")
)
override fun readFrom() = response.content
})
}
response is a io.ktor.client.statement.HttpResponse returned by HttpStatement.execute.
The reason I'm doing this is that the backend service I'm developing with Ktor is a gateway that enforces authentication for clients coming from the open internet towards services that are behind this gateway.
Most of the time this code works nicely but I'm starting to have some weird behavior where clients that are downloading large amounts of data abruptly disconnect in the middle of the download due to unexpected end of stream errors on the client side. On the server side there are no error messages whatsoever and it looks like a graceful connection close.
Is there anything that is fundamentally broken with this code? I'm running this code in Amazon EKS environments where some have zero problems while others are absolutely broken.
From your code, it looks like your execute method is asynchronous. Ktor, on the other hand, is built around coroutines, so you need to make the coroutine wait for execute to finish. This can be done with suspendCoroutine, for example:
val result = suspendCoroutine<OutgoingContent.ReadChannelContent> { continuation ->
serviceClient.retrieveCourseZip(id, version).execute {
continuation.resume(object : OutgoingContent.ReadChannelContent() { /*...*/ })
}
}
call.respond(result)
Another option is to make execute a suspend function that will return the result itself. If asynchronous code is used inside - this can be done exactly the same way with suspendCoroutine, but you say you are using a Ktor client that has to do all requests with coroutines, so your code can probably be simplified, but it is hard to tell without looking at it.

Service Now Api integration using client side script

I tried to integrate external soap based api using servicenow client side scipt options. My intention is to initiate an external api call when an incident is created.
But i am getting uncaught reference error sn_ws is not defined exception.
function onSubmit() {
try {
var s = new sn_ws.SOAPMessageV2('global.IQTrack', 'VerifyApiKey');
s.setStringParameterNoEscape('VerifyApiKey.apiKey', 'dfghdhgdjh');
var response = s.execute();
var responseBody = response.getBody();
var status = response.getStatusCode();
}
catch(ex) {
alert(ex);
}
}
Is this the way to initiate api call? If it is so why it is getting sn_ws is not defined.
That's because sn_ws is a server-side API.
You need to either use GlideAjax, or a client-side webservices API such as XMLHttpRequest.
You can find an excellent article on GlideAjax, here: http://snprotips.com/blog/2016/2/6/gliderecord-client-side-vs-server-side
If your aim is to initiate the message once a ticket is created, then you should definitely be doing this server-side, not in a client script.
I hope,sn_ws is a server-side API.
I think GlideAjax method will help you to get rid of this issues.
please go through below links,I think it will help you to sort out this issues.
http://wiki.servicenow.com/index.php?title=GlideAjax#gsc.tab=0
And alternative is use client-side webservices API like XMLHttpRequest

How to ignore request body and redirect request using akka-http

Using latest akka-http I want to implement an endpoint which will redirect all incoming upload file requests without consuming request entity.
Current implementation is using :
put {
extractRequest { r: HttpRequest =>
onComplete(r.discardEntityBytes().future) { done =>
redirect(Uri("http://example.com"), TemporaryRedirect)
}
}
}
The problem is that it waits until whole http request body is received (discarded) and only after that sends redirect response. From client perspective it means uploading file twice. I tried to add withSizeLimit(0) to request entity, but it introduces early response problem.
Related documentation:
http://doc.akka.io/docs/akka/2.4.11/scala/http/implications-of-streaming-http-entity.html
http://doc.akka.io/docs/akka/2.4.11/scala/http/routing-dsl/directives/route-directives/redirect.html#description
I did something similar, today. Are you sure that the discarding needs to be completed prior to the redirect?
How about this:
put {
extractRequest { r: HttpRequest =>
r.discardEntityBytes() // runs wild
redirect(Uri("http://example.com"), TemporaryRedirect)
}
}
.discardEntityBytes is used to try to "cancel" unnecessary network traffic that the receiver won't need. Here, we set it up asap. It runs on the background (I hope), and returning the redirect would happen also asap.
I'd be interested to hear, whether this changed anything...

Intercept Requests With Custom Responses in PhantomJS?

Is there a way to intercept a resource request and give it a response directly from the handler? Something like this:
page.onRequest(function(request){
request.reply({data: 123});
});
My use case is for using PhantomJS to render a page that makes calls to my API. In order to avoid authentication issues, I'd like to intercept all http requests to the API and return the responses manually, without making the actual http request.
onResourceRequest almost does this, but doesn't have any modification capabilities.
Possibilities that I see:
I could store the page as a Handlebars template, and render the data into the page and pass it off as the raw html to PhantomJS (instead of a URL). While this would work, it would make changes difficult since I'd have to write the data layer for each webpage, and the webpages couldn't stand alone.
I could redirect to localhost, and have a server there that listens and responds to the requests. This assumes that it would be ok to have an open, un-authenticated version of the API on localhost.
Add the data via page.evaluate to the page's global window object. This has the same problems as #1: I'd need to know a-priori what data the page needs, and write server side code unique to each page.
I recently needed to do this when generating pdfs with phantom js.
It's slightly hacky, but seems to work.
var page = require('webpage').create(),
server = require('webserver').create(),
totallyRandomPortnumber = 29522,
...
//in my actual code, totallyRandomPortnumber is created by a java application,
//because phantomjs will report the port in use as '0' when listening to a random port
//thereby preventing its reuse in page.onResourceRequested...
server.listen(totallyRandomPortnumber, function(request, response) {
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json;charset=UTF-8');
response.write(JSON.stringify({data: 'somevalue'}));
response.close();
});
page.onResourceRequested = function(requestData, networkRequest) {
if(requestData.url.indexOf('interceptme') != -1) {
networkRequest.changeUrl('http://localhost:' + totallyRandomPortnumber);
}
};
In my actual application I'm sending some data to phantomjs to overwrite request/responses, so I'm doing more checking on urls both in server.listen and page.onResourceRequested.
This feels like a poor-mans-interceptor, but it should get you (or whoever this may concern) going.