Geocoding requests to HERE API randomly fails - spring-boot

I am trying to geocode addresses with HERE API. I am not free plan. I try following code (Spring Boot in Kotlin):
override fun geocode(address: Address): Coordinate? {
val uriString = UriComponentsBuilder
.fromHttpUrl(endpoint)
.queryParam("app_id", appId)
.queryParam("app_code", appCode)
.queryParam("searchtext", addressToSearchText(address))
.toUriString()
logger.info("Geocode requested with url {}", uriString)
val response = restTemplate.getForEntity(uriString, String::class.java)
return response.body?.let {
Klaxon().parse<GeocodeResponse>(it)
}?.let {
it.Response.View.firstOrNull()?.Result?.firstOrNull()
}?.let {
Coordinate(
latitude = it.Location.DisplayPosition.Latitude,
longitude = it.Location.DisplayPosition.Longitude
)
}.also {
if (it == null) {
logger.warn("Geocode failed: {}", response.body)
}
}
}
It turned out that when I call this method many times in a row, some requests returns empty responses, like this:
{
"Response":{
"MetaInfo":{
"Timestamp":"2019-04-18T11:33:17.756+0000"
},
"View":[
]
}
}
I could not figure out any rule why some requests fail. It seems to be just random.
However, when I try to call same URLs with curl of in my browser, everything works just fine.
I guess there is some limit for amount requests per seconds, but I could not find anything in HERE documentation.
Does anyone have an idea about the limit? Or may it be something else?

Actually, there was a problem with my code. Requests were failing for addresses having "special" symbols like ü and ö. The problem was with building request URL
val uriString = UriComponentsBuilder
.fromHttpUrl(endpoint)
.queryParam("app_id", appId)
.queryParam("app_code", appCode)
.queryParam("searchtext", addressQueryParam(address))
.build(false) // <= this was missed
.toUriString()

Related

UnknownHostException when trying to connect using websocket

I have a use case where I need to send 2 requests to the server. The output of first request is used in second request so the calls have to be synchronous. I am using ktor (OkHttp)client websocket for this. I am failing at first attempt to even connect to the server with this error
Exception in thread "main" java.net.UnknownHostException: https: nodename nor servname provided, or not known
I suspect I haven't split my url properly and thats why its not able to connect to host.
Couple of qns
Is there any benefit to using websocket instead of using 2 separate Http requests?
Is there a way I can just pass URL to the websocket request?
Best and easiest way to get response and send another request?
I have been able to find very limited documentation on ktor client websocket.
const val HOST = "https://sample.com"
const val PATH1 = "/path/to/config?val1=<val1>&val2=<val2>"
const val PATH2 = "/path/to/config?val=<response_from_first_req>"
fun useSocket() {
val client = HttpClient() {
install(WebSockets)
}
runBlocking {
client.webSocket(method = HttpMethod.Get, host = HOST, path = PATH1) {
val othersMessage = incoming.receive() as? Frame.Text
println(othersMessage?.readText())
println("Testing")
}
}
client.close()
}
Thanks in advance.

Convert data class to map to test http GET response body

I'm trying to test a GET to get all the StatusMapping objects created, however, I'm not sure what's the best approach to test this.
The response is returning a map whereas I was expecting a list of StatusMapping objects instead.
Should I convert the requests to a map?
Here's the Service code:
fun getAll(): ResponseEntity<List<StatusMapping>> {
return ResponseEntity<List<StatusMapping>>(statusMappingRepository.findAll(), HttpStatus.OK)
}
Here's the test
#Test
fun `Get all mappings created`() {
val requests = listOf(
StatusMapping("available", "available"),
StatusMapping("unavailable", "unavailable")
)
requests.forEach { statusMappingService.createMapping(it.toStatusMappingRequest()) }
val response = restTemplate.getForEntity(getRootUrl(), List::class.java)
assertEquals(response.body, requests)
}
Here's the error that I'm getting:
Expected :[{source=available, target=available}, {source=unavailable, target=unavailable}]
Actual :[StatusMapping(source=available, target=available), StatusMapping(source=unavailable, target=unavailable)]
Please start with replacing
val response = restTemplate.getForEntity(getRootUrl(), List::class.java)
with
val response = restTemplate.exchange(
getRootUrl(),
HttpMethod.GET,
null,
object : ParameterizedTypeReference<List<StatusMapping>>() {})
Assuming that restTemplate is instance of TestRestTemplate

#RequestHeader behaviour not as expected; working without required User-Agent

I have a project set up using Spring Boot with Kotlin to make REST APIs.
I'm trying to use the #RequestHeader to recognize the User-Agent. The said header is required=true -
#PostMapping("details", produces = ["application/json"])
fun addInfo(#RequestHeader(name = "User-Agent", required = true) userAgent: String,
#Valid #RequestBody podEntity: PodEntity): ResponseEntity<String> {
pod.addPod(podcastEntity)
return ResponseEntity<String>("{ \"status\":\"Added\" }", HttpStatus.CREATED)
}
Problems -
However, even if I'm not sending the User-Agent header, the API is working and adding data. But, if I change the header to any other non default names, like, user or request-source, the required=true requirement is enforced to and the API does not work. Does it mean that default headers cannot be made mandatory using the required tag?
The other problem is that in the case of custom header, when the required header is missing for the request, the API fails by giving 400 error code but does not throw any exception. I was expecting HttpClientErrorException for my junit test case but on checking the console, I see no exception. Adding #Throws is also not helping. How do enable my function to throw an exception when the required header is missing?
Unit test -
#Test
fun test_getAll_fail_missingHeaders() {
val url = getRootUrl() + "/details/all"
val headers = HttpHeaders()
val request = HttpEntity(pod, headers)
try {
restTemplate!!.postForEntity(url, request, String::class.java)
fail()
} catch (ex: HttpClientErrorException) {
assertEquals(400, ex.rawStatusCode);
assertEquals(true, ex.responseBodyAsString.contains("Missing request header"))
}
}

Can Websocket and normal get route be same in Akka Http?

I do have a scenario where I will want my websocket route and get route paths to be the same. Is it possible in Akka Http?
Consider the below mentioned code:
def flow: Flow[Message, Message, Any] =
Flow.fromSinkAndSource(Sink.ignore,
Source.single(TextMessage.Strict("Hello from websocket")))
val route =
path("hello") {
get {
complete(HttpEntity(ContentTypes.`application/json`,"Simple hello"))
}
} ~ path("hello") {
handleWebSocketMessages(flow)
}
If, through a websocket client, I access ws://localhost:8080/hello, I get an websocket error. But a normal curl request gives the result of Simple hello. Is it possible to somehow achieve both actions on same route.
Something along the lines of the below should do
val route = path("hello") {
optionalHeaderValueByType[UpgradeToWebSocket](()) {
case Some(upgrade) => complete(upgrade.handleMessages(flow))
case None => get {
complete("Simple hello")
}
}
}

Caching issue with WebClient/HttpClient

I'm developing a code to poll a Bitstamp exchange ticker every 30 seconds. This is a code I have:
public IObservable<string> Stream(Uri way, WebClient wc)
{
Func<IObserver<string>, Task> Fun = async Observer =>
{
var res = await wc.DownloadStringTaskAsync(way);
Observer.OnNext(value: res);
};
return Observable.Create<string>(Fun);
}
public IObservable<string> GetDelay(int secs)
{
var exe = TimeSpan.FromSeconds(secs);
return Observable.Empty<string>("x").Delay(exe);
}
Stream(new Uri("https://bitstamp.net/api/ticker"), new WebClient { }).Concat(GetDelay(30))
.Repeat(5).Subscribe(res => Debug.WriteLine("got result: {0}", res));
The problem is that WebClient (and HttpClient, too) both return cached results after the first call, it can be seen by the same timestamp:
got result: {"high": "690.00", "last": "645.10", "timestamp": "1387715532" ... }
got result: {"high": "690.00", "last": "645.10", "timestamp": "1387715532" ... }
...
Even after turning the networks off they return the result normally so obviously they cache it somewhere. Adding something like "?cache=random" does not work because request parameters are not allowed for ticker on Bitstamp. Setting Headers[HttpRequestHeader.CacheControl] = "no-cache" for WebRequest does not work either.
How can I fix this weird caching behavior?
Solved by setting wc.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString(); before each subsequent call.
For Windows Phone Setting with Language=Chinese and "date+time" is not "24-hour clock" ,
the code wc.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString() causes exception throwing since DateTime.ToString() generates Chinese characters "下午" in the header.
The safer solution is to output the date time format as RFC1123 pattern:
req.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString("R");
This will ensure the date time is in format of "Sat, 05 Jul 2014 13:38:28 GMT", and there will no any Chinese characters within the HTTP headers.

Resources