How to properly set an API call in QML using XMLHttpRequest - c++

I am building a small weather API as exercise to use QML and properly operate an API call using OpenWeather and you can see there a typical API response.
The problem I am having is that I can't get the API call to work. After setting a minimal example with some cities that you can see below, right next to the city it should appear the symbol of the weather, but it does not happen. The list of the icon can be found here. Source code of the MVE can be found here for completeness.
The error from the compiler: qrc:/main.qml:282: SyntaxError: JSON.parse: Parse error
This is what is happening
This is what is expected
Typical API JSON response can be found both here and below:
{
"coord": {
"lon": -122.08,
"lat": 37.39
},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
"base": "stations",
"main": {
"temp": 282.55,
"feels_like": 281.86,
"temp_min": 280.37,
"temp_max": 284.26,
"pressure": 1023,
"humidity": 100
},
"visibility": 16093,
"wind": {
"speed": 1.5,
"deg": 350
},
"clouds": {
"all": 1
},
"dt": 1560350645,
"sys": {
"type": 1,
"id": 5122,
"message": 0.0139,
"country": "US",
"sunrise": 1560343627,
"sunset": 1560396563
},
"timezone": -25200,
"id": 420006353,
"name": "Mountain View",
"cod": 200
}
Below a snippet of code related to the API call:
main.qml
// Create the API getcondition to get JSON data of weather
function getCondition(location, index) {
var res
var url = "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}"
var doc = new XMLHttpRequest()
// parse JSON data and put code result into codeList
doc.onreadystatechange = function() {
if(doc.readyState === XMLHttpRequest.DONE) {
res = doc.responseText
// parse data
var obj = JSON.parse(res) // <-- Error Here
if(typeof(obj) == 'object') {
if(obj.hasOwnProperty('query')) {
var ch = onj.query.results.channel
var item = ch.item
codeList[index] = item.condition["code"]
}
}
}
}
doc.open('GET', url, true)
doc.send()
}
In order to solve this problem I consulted several sources, first of all : official documentation and the related function. I believe it is correctly set, but I added the reference for completeness.
Also I came across this one which explained how to simply apply XMLHttpRequest.
Also I dug more into the problem to find a solution and also consulted this one which also explained how to apply the JSON parsing function. But still something is not correct.
Thanks for pointing in the right direction for solving this problem.

Below the answer to my question. I was not reading properly the JSON file and after console logging the problem the solution is below. code was correct from beginning, only the response needed to be reviewed properly and in great detail being the JSON response a bit confusing:
function getCondition() {
var request = new XMLHttpRequest()
request.open('GET', 'http://api.openweathermap.org/data/2.5/weather?q=London&units=metric&appid=key', true);
request.onreadystatechange = function() {
if (request.readyState === XMLHttpRequest.DONE) {
if (request.status && request.status === 200) {
console.log("response", request.responseText)
var result = JSON.parse(request.responseText)
} else {
console.log("HTTP:", request.status, request.statusText)
}
}
}
request.send()
}
Hope that helps!

In your code, your url shows this: "api.openweathermap.org/data/2.5/weather?id={city id}&appid={your api key}". You need to replace {city id} and {your api key} with real values.
You can solve it by providing an actual city ID and API key in your request URL

Related

GCP Video Intelligence - batchPredict error

Following this documentation, when requesting a batchPredict I run into this error via API
{
"error": {
"code": 13
"message": "internal",
}
}
Additionally, here's a screenshot screenshot of the error I see when I try to use the "Test & Use" tab. Neither of which are descriptive, so I'm not sure where the error lies.
In the request, I include the path to my CSV file in the Google Storage, which links to a video in the same bucket. Here's the contents of the CSV:
gs://XXXXXXXXXXXX/movie1.mov,0,inf
gs://XXXXXXXXXXXX/movie2.mov,0,inf
I also include the path to a /Results folder (in the same bucket) to save the predictions.
Code making the call:
const client = new PredictionServiceClient();
async function batchPredict() {
const request = {
name: client.modelPath('project-id-xxxxxx', 'us-central1', 'VOTxxxxxxxxxx'),
inputConfig: {
gcsSource: {
inputUris: ['gs://XXXXXXXXXXXX/apitest.csv'],
},
},
outputConfig: {
gcsDestination: {
outputUriPrefix: 'gs://XXXXXXXXXXXX/results/',
},
},
};
Please let me know if I need to provide any more detail.
The possible root cause is one of those two:
There is an issue somewhere in your code. So, if your code is not the same as below, I suggest that you try it out (changing the appropriate variables of course).
There is something wrong with your model, which is the most probable root cause (as per the error message itself).
So, if it is not your code, you should create a private issue report on issue-tracker explaining your issue and giving as much details as possible on it as well as your use case and impact.
As it is private, only Googlers and you will have access to it so feel free to share your project and model IDs.
Here is what I did to try to reproduce your issue (be sure to follow the before you begin guide):
I have trained a model on gs://YOUR_BUCKET/TRAINING.csv
TRAIN,gs://automl-video-demo-data/traffic_videos/traffic_videos_train.csv
TEST,gs://automl-video-demo-data/traffic_videos/traffic_videos_test.csv
Predicted on a couple of images on gs://YOUR_BUCKET/VIDEOS_TO_ANNOTATE.csv (inputUri):
gs://automl-video-demo-data/traffic_videos/highway_078.mp4, 0,inf
gs://automl-video-demo-data/traffic_videos/highway_079.mp4,10.00000,15.50000
using the Node.js predict example from the tutorial:
/**
* TODO(developer): Uncomment these variables before running the sample.
*/
const projectId = 'YOUR_PROJECT';
const location = 'us-central1';
const modelId = 'VOTXXXXXXXXXXXXXXXXXX';
const inputUri = 'gs://YOUR_BUCKET/VIDEOS_TO_ANNOTATE.csv';
const outputUri = 'gs://YOUR_BUCKET/outputs/';
// Imports the Google Cloud AutoML library
const {PredictionServiceClient} = require('#google-cloud/automl').v1beta1;
// Instantiates a client
const client = new PredictionServiceClient();
async function batchPredict() {
// Construct request
const request = {
name: client.modelPath(projectId, location, modelId),
inputConfig: {
gcsSource: {
inputUris: [inputUri],
},
},
outputConfig: {
gcsDestination: {
outputUriPrefix: outputUri,
},
},
};
const [operation] = await client.batchPredict(request);
console.log('Waiting for operation to complete...');
// Wait for operation to complete.
const [response] = await operation.promise();
console.log(
`Batch Prediction results saved to Cloud Storage bucket. ${response}`
);
}
batchPredict();
Note that I have also tried the REST & CMD LINE predict example.
And in both cases, it worked well and I received a correct response:
Nodejs prediction's response:
Waiting for operation to complete...
Batch Prediction results saved to Cloud Storage bucket. [object Object]
REST & CMD LINE prediction's response:
{
"name": "projects/XXXXXXXXXX/locations/us-central1/operations/VOTXXXXXXXXXXXXXXX",
"metadata": {
"#type": "type.googleapis.com/google.cloud.automl.v1beta1.OperationMetadata",
"createTime": "2021-04-16T08:09:52.102270Z",
"updateTime": "2021-04-16T08:09:52.102270Z",
"batchPredictDetails": {
"inputConfig": {
"gcsSource": {
"inputUris": [
"gs://MY_BUCKET/VIDEOS_TO_ANNOTATE.csv"
]
}
}
}
}
}

AWS Pinpoint: Set APNS "mutable-content": 1

AWS Poinpoint APNS by default sets "mutable-content": 0.
I am using Node.js.
Below works fine, but mutable-content is always 0. "mutable-content": 0:
var messageRequest = {
'Addresses': {
https://forums.aws.amazon.com/: {
'ChannelType': channelType
}
},
'MessageConfiguration': {
'APNSMessage': {
'Action': action,
'Body': message,
'Priority': priority,
'SilentPush': silent,
'Title': title,
'TimeToLive': ttl,
'Url': url,
}
}
Below is the payload I get when an APNS is sent using the above setup
["aps": {
alert = {
body = "TEST";
title = "Test message sent from Amazon Pinpoint.";
};
"content-available" = 1;
"mutable-content" = 0;
}, "data": {
pinpoint = {
deeplink = "https://www.example.com";
};
}]
How can I set "mutable-content": 1 for an APNS through AWS Pinpoint?
There is no documentation but this worked for me after some trial and error:
var payload = {
"aps": {
"alert": {
"title": "Bold text in the notification",
"body": "Second line in the notification"
},
"sound": "default",
"mutable-content": 1
}
};
var messageRequest = {
Addresses: {
[token]: {
ChannelType: "APNS",
},
},
MessageConfiguration: {
APNSMessage: {
RawContent: JSON.stringify(payload),
},
},
};
Just replace their template with RawContent and create the payload as you would normally. Can refer to apple docs on how to create the raw payload. You can also adjust content-available key using this method. Here is the link to how to create a payload with json:
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
I know this is a bit old, but just ran into this issue and wanted to share my solution.
I found that setting the "MediaUrl" parameter to a non-empty string would cause pinpoint to send "mutable-content": 1
I did not see this in any of the pinpoint documentation.

Check for null values in request body using Wiremock

I am trying to setup a wiremock stub that will return a 400 error if any field has a null value in the json payload. Basically to simulate a Bad Request. I've been trying with a regex that matches any lowercase string for the json key but it doesn't seem to like it. I can't find any examples of what I want online so not sure if it's even possible.
My Bad Request body:
{
"cat": null,
"dog": {
"id": 1344
},
"horse": {
"id": 1
},
"fish": 1
}
My Stub:
wireMockServer.stubFor(post(urlEqualTo("/sample-api"))
.withRequestBody(matchingJsonPath("$.^[a-z]*", equalTo(null)))
.willReturn(aResponse()
.withStatus(400)
.withHeader("Content-Type", "application/json")))
In this example I would expect the stub to match "cat" as the value of it is null. This isn't the case. Can anyone tell me what I'm doing wrong?
In the WireMock documentation on Request Matching the section on JSON Path matching. In the source code there is a reference to com.jayway.jsonpath.JsonPath library used. The build.gradle refers to version 2.4.0. The documentation for the Jayway JSON Path library can be found on their Github project page. There is a good, but by no means perfect online evaluator here.
The WireMock documentation only shows support for Regular Expression for the node values in the form of the "matchesJsonPath". In the Jayway documenatation there is an online example: $..book[?(#.author =~ /.*REES/i)]. For this reason the only approach is to name all the nodes that are not allowed to be null.
In the below example mapping all the mentioned nodes will be tested, regardless of their depth (see #id). This mapping will not trigger if all the mentioned nodes are not null, but some unmentioned ones are.
{
"request": {
"urlPattern": "/sample-api",
"method": "GET",
"bodyPatterns" : [ {
"matchesJsonPath" : "$..[?(#.cat == null || #.dog == null || #.horse == null || #.fish == null || #.id == null)]"
} ]
},
"response": {
"status": "400",
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"jsonBody": {
"message": "some sapi message"
}
}
}
If you weren't aware of all possible keys, you could use a Custom Request Matcher to check if the request body contained any null values, and if so, return your 400 error. I'd recommend creating your own class, something that resembles...
public class BodyNullCheck extends RequestMatcherExtension {
#Override
public MatchResult match(Request request, Parameters parameters) {
JSONParser parser = new JSONParser();
try {
JSONObject body = (JSONObject) parser.parse(request.getBody().toString());
for(Iterator iterator = body.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
if (body.get(key) == null) {
return MatchResult.of(true);
}
}
} catch (ParseException ex) {
ex.printStackTrace();
}
return MatchResult.of(false);
}
}
The above takes the request body and converts it to a JSONObject, and then iterates over all keys in the JSONObject. If any of their values are null, then we will return true. If after iterating over all of them, a null value isn't found, we return false.

Alexa function works in AWS lamda, but not from the Service Simulator

I am relatively new to AWS and Alexa skills. I am building a simple custom skill that gives you a dressing advice depending on the weather.
I have 2 custom intents : dressingTodayIntent & dressingTomorrowIntent. In the Service Simulator of the developer portal, my two intents don't work, I do get a lambda response though, but with an undefined outputSpeech, like this:
{
"version": "1.0",
"response": {
"outputSpeech": {
"type": "SSML",
"ssml": "<speak> undefined </speak>"
},
"card": null,
"reprompt": null,
"speechletResponse": {
"outputSpeech": {
"id": null,
"ssml": "<speak> undefined </speak>"
},
"card": null,
"directives": null,
"reprompt": null,
"shouldEndSession": true
}
},
"sessionAttributes": {}
}
Could it be a scope issue in my intent code?
'DressingTodayIntent': function() {
var dressingAdvice;
var speechOutput = getJSON('https://api.darksky.net/forecast/9e0495a835ed823a705a9a567eee982a/48.861317,2.348764?units=si&exclude=currently,minutely,hourly,alerts,flags',
function(err, forecast) {
if (err) {
console.log('Error occurred while trying to retrieve weather data', err);
} else {
dressingAdvice = getDressingAdvice(forecast, true);
console.log("one " + dressingAdvice);
}
console.log("two " + dressingAdvice);
return dressingAdvice;
});
console.log("three " + speechOutput);
this.response.cardRenderer("Your dressing advice for today:", speechOutput);
this.response.speak(speechOutput);
this.emit(':responseReady');
},
In AWS Lambda, I see a correct output for the first 2 logs, and an error for the 3rd one:
first log: "one " + dressingAdvice, as expected
second log: "two " + dressingAdvice, as expected
third log: "three " + undefined
Thank you for you help!
When you say "tested from AWS Lambda", I assume that you mean using the AWS console to send a JSON test message to the Lambda, then looking at the response JSON to determine if it is correct?
If so, make sure that it matches the JSON sent to/from the Alexa test page in the dev portal. Sounds like they might be different.
Also, make sure that you are linked to the correct ARN in the Alexa skill.
The undefined is likely a variable scope issue in the code.
I noticed in your response that you don't have any sessionAttributes. Is your code setting or pulling the value for the response from a session value? If so, the values need to be sent back with the sessionAttributes.
I figured out what was wrong, I needed to move the response code into the callback function, like this:
'DressingTodayIntent': function() {
var speechOutput;
var self = this;
var dressingAdvice = getJSON('https://api.darksky.net/forecast/9e0495a835ed823a705a9a567eee982a/48.861317,2.348764?units=si!ude=currently,minutely,hourly,alerts,flags',
function(err, forecast) {
if (err) {
console.log('Error occurred while trying to retrieve weather data', err);
} else {
speechOutput = getDressingAdvice(forecast, true);
}
self.response.cardRenderer("Your dressing advice for today:", speechOutput);
self.response.speak(speechOutput);
self.emit(':responseReady');
});
},

Extracting global variable value from brackets

I am sending a POST request, which I'm getting in the response an attribute named "value" that its value is a number with brackets. I need to use the number without the brackets for my next API request.
Here is what I get in the response of my request:
{
"additionalAttributes": {
"map": [
{
"key": "RESULT_IDS",
"value": "[26913648997439042205288611421953968843]"
}
]
}
Here is what I've updated in Tests tab of the request in order to save it as a global variable:
tests["Status code is 200"] = responseCode.code === 200;
if (responseCode.code === 200) {
try {
var campaign_data = JSON.parse(responseBody),
campaignValue = campaign_data.additionalAttributes.map[0].value;
} catch(e) {
console.log(e);
}
postman.setGlobalVariable("campaignValue",campaignValue);
}
Can you explain please how can I have the value 26913648997439042205288611421953968843 without the brackets saved into a global variable?
Many thanks.
You can use the integrated lodash lib of the Postman sandbox:
var campaignValueRaw = campaign_data.additionalAttributes.map[0].value;
var campaignValue = _.trimRight(_.trimLeft(campaignValueRaw, '[') , ']');