HTTP Post request not going through in AWS Lambda - node.js

I am trying to add an item to my Todoist project through an Alexa skill in AWS Lambda. I am very new to all of these technologies so forgive me if the fix is incredibly obvious. When I ask Alexa to invoke my addZipcode skill, it fails. This is what I have (excluding some stuff that is in all Alexa Lambda functions):
Alexa stuff
...
const handlers = {
'LaunchRequest': function() {
this.emit('AMAZON.HelpIntent');
},
'addZipcode': function() {
const newZip = this.event.request.intent.slots.zipcode.value;
const speechOutput = newZip;
var http = require("https");
function postZip(newZip) {
var options = {
"method": "POST",
"hostname": [
"beta",
"todoist",
"com"
],
"path": [
"API",
"v8",
"tasks"
],
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}
};
var req = http.request(options, function(res) {
var chunks = [];
res.on("data", function(chunk) {
chunks.push(chunk);
});
res.on("end", function() {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.write(JSON.stringify({ content: newZip, project_id: XXXXXXXXXX }));
req.end();
}
postZip(newZip);
this.response.cardRenderer(SKILL_NAME, newZip);
this.response.speak(speechOutput);
this.emit(':responseReady');
},
.... cont
I get the resulting error when I try to run the skill with Alexa:
Response:
{
"errorMessage": "hostHeader.replace is not a function",
"errorType": "TypeError",
"stackTrace": [
"Agent.addRequest (_http_agent.js:130:39)",
"new ClientRequest (_http_client.js:159:16)",
"Object.exports.request (http.js:31:10)",
"Object.exports.request (https.js:199:15)",
"postZip (/var/task/index.js:72:28)",
"Object.addZipcode (/var/task/index.js:88:9)",
"emitNone (events.js:86:13)",
"AlexaRequestEmitter.emit (events.js:185:7)",
"AlexaRequestEmitter.EmitEvent (/var/task/node_modules/alexa-sdk/lib/alexa.js:216:10)",
"AlexaRequestEmitter.ValidateRequest (/var/task/node_modules/alexa-sdk/lib/alexa.js:181:23)"
]
}
I tried searching for more information about hostHeader.replace or even just hostHeader but to no avail. When I surround my postZip function with
exports.handler = function(event, context, callback) {}
the skill actually works, but the Post request does not go through (as in, the new zipcode is not added as a new task on my Todoist). I'm pretty sure the Post request code itself is correct because I ran it through Postman and the zipcode was added.
Please help me understand why it doesn't work.

It's hard to tell what causes that error. But the node docs say, that hostname as well as path are supposed to be nothing but strings and not arrays as it's the case in your code.
So what I'd do first is to change your code to this:
var options = {
"method": "POST",
"hostname": "beta.todoist.com",
"path": "/API/v8/tasks",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer " + token
}

Related

Add Icon to Onesignal push notification using NodeJS Rest API

I wanted to add icon in push notification from rest API. I am using NodeJS as my backend. I have already tried options mention in following code :
let message = {
app_id: process.env.ONE_SIGNAL_APP_ID,
headings: { "en": "MyAPP" },
contents: { "en": "Hello" },
large_icon: data.picture ? process.env.ONE_SIGNAL_IMAGE_URL + data.picture : "",
include_player_ids: ["playerId"]
};
let headers = {
"Content-Type": "application/json; charset=utf-8",
"Authorization": process.env.ONE_SIGNAL_AUTHORIZATION
};
let options = {
host: "onesignal.com",
port: 443,
path: "/api/v1/notifications",
method: "POST",
headers: headers
};
let req = https.request(options, function (res) {
res.on('data', function (message) {
// console.log(JSON.parse(message));
});
});
req.on('error', function (e) {
// console.log(e);
});
req.write(JSON.stringify(message));
req.end();
I have putted correct image URL which is "https://mydomain/icons/Icon_kitchen.png" and if I hit this URL in browser I get image, too.
I have also tried samll_icon option but not succeed. I am not able to figure out the problem. Kindly help me out. Thanks in advance.

Azure Cognitve Search upload document via browser cors issue

I want to use the cognitive search to store usernames and levels of a browser game. There should be an option to create a new username in the web frontend. The idea is, that the user enters a username and submits via button. Then these username should be saved in a cognitive search document.
MyiIssue: I am getting a cors error in browser console:
Access to XMLHttpRequest at 'https://XXXXXXX.search.windows.net/indexes/users/docs/index' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The same issue occurs when I deploy it to a azure static web app.
The CORS settings at azure allow all origins.
Regarding to Azure Search and CORS issue with PUT, it is not possible to modify the data in this way. I am new to programming, how can it be done in another way (serverless function?)?
My code:
OnSubmitCreate(){
console.log('Create started')
let data = JSON.stringify({
"value": [
{
"#search.action": "mergeOrUpload",
"id": this.createName,
"username": this.createName,
"level": 1
}
]
});
let config = {
method: 'post',
url: 'https://XXXXXXXXXXX.search.windows.net/indexes/users/docs/index?api-version=2020-06-30',
headers: {
'api-key': 'XXXXXXXXXXXXXXXXXxx',
'Content-Type': 'application/json'
},
data : data
};
Thanks in advance!
I`ve solved it with creating a azure function using javascript. When I call the URL, the write to cognitive search will be done.
So I technically am doing a http rest call (push).
index.js
var https = require('https');
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
var username = context.bindingData.username;
var level = context.bindingData.level;
let data = JSON.stringify({
"value": [
{
"#search.action": "mergeOrUpload",
"id": username,
"username": username,
"level": level
}
]
})
var options = {
host: 'XXXXXXXXXXX.search.windows.net',
path: '/indexes/users/docs/index?api-version=2020-06-30',
port: 443,
method: 'POST',
headers: {
'api-key': 'XXXXXXXXXXXX',
'Content-Type': 'application/json'
},
body: data
};
var req = https.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write(data);
req.end();
context.res = {
// status: 200, /* Defaults to 200 */
body: req
};
}
function.json
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
],
"route": "data/{username:alpha}/{level:int?}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}

Is there a "standard" way to construct an HTTP GET request for an API?

I'm trying to work with a new API provided by a messaging service.
Their documentation says their API can be queried with a "standard" HTTPS "GET" request.
Now to me, GET request URLS look like this:
https://api.domain.com/path/subpath/?params&otherparams
I should be able to construct the above using their "documentation", which is an example given in node.js:
var http = require("https");
var options = {
"method": "GET",
"hostname": "api-cpaas.dotdigital.com",
"auth": "apiuser:password",
"port": null,
"path": "/messages/{Message Id}",
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
}
};
var req = http.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.end();
Example message ID: 6df8b42b-7165-4099-bd9b-356b2f41f1a9
Published here:
https://developer-cpaas.dotdigital.com/reference/getting-message-statuses
But no matter what I try I always get a "404" error.
I don't know node.js so I can't interpret their example, and they don't offer any other examples. What should the actual GET request for this service should look like?

Node-Red sending the file in http-request node is not working

I am trying to send the file with the http-request node but it is not working.
Please find the following image of Node-red flow.
In the Request Body node I have added the following code.
const inputFile = msg.payload;
const dataJson =
{
'name': 'testName',
'description':'testdescription',
'inputfile': inputFile
};
msg.payload = dataJson;
msg.url = 'myAPIurl';
msg.headers = {
'authorization': 'Bearer TOKEN Here',
'cookie': 'Cookie here',
'content-type': 'multipart/form-data;'
};
return msg;
This is giving bad request error.
In the Read File node I tried choosing both options A single UTF8-String and a single Buffer Object still I got the same error
But I tried to call the API inside function node using the request module. It is giving the proper response.
const request = global.get("request");
const fs = global.get("fs");
const url = 'API';
const tkn = 'TOken Here';
const cookie = 'cookie here';
const fl = fs.createReadStream('/tmp/node-red/app/data/filename.txt');
var options = {
method: 'POST',
url: url,
headers: {
'Authorization': tkn,
'Cookie': cookie,
},
formData: {
"name": "test121",
"description": "",
inputfile: fl
}
};
request(options, function (err, resp, body) {
console.log(body);
});
return msg;
I am not sure where I am making the mistake if I use http-request node.
From the sidebar docs for the http-request node:
File Upload
To perform a file upload, msg.headers["content-type"] should be set
to multipart/form-data and the msg.payload passed to the node must
be an object with the following structure:
{
"KEY": {
"value": FILE_CONTENTS,
"options": {
"filename": "FILENAME"
}
}
}
The values of KEY, FILE_CONTENTS and FILENAME should be set to the
appropriate values.
Following this doc, your msg.payload is wrong, it should look something like:
msg.payload: {
"name": "testName",
"description": "description",
"inputfile": {
"value": inputfile,
"options": {
"filename": "filename.txt"
}
}
}

Alexa smart home skill: problem with discover devices

I have problems with the discovery of devices of my Alexa Smart Home skill.
Steps which work:
activate Alexa skill
OAuth login screen appears. After successful login, the discovery of devices is triggered
in the lambda function I get the bearer token which I use to call the .../devices endpoint
I get the devices from the REST endpoint and construct the payload as described in https://developer.amazon.com/de/docs/smarthome/steps-to-build-a-smart-home-skill.html
The payload (same structure as in the example) is provided to context.succeed
My problem:
After the Alexa Skill returns from discovery of devices task, no new devices are visible in the Alexa Skill.
When I use the code from the sample (where no request to an external Rest API happens), the device is visible in the Alexa skill after the Alexa discovery task.
var https = require('https');
const AWS = require('aws-sdk');
exports.handler = function(request, context) {
var options = {
method: 'GET',
hostname: 'xyz.azurewebsites.net',
path: '/devices',
headers: {
Authorization: 'Bearer ' + request.directive.payload.scope.token,
'Content-Type': 'application/json'
}
};
var req = https.get(options, (response) => {
var data = '';
response.setEncoding('utf8');
response.on('data', function(x) { data += x; } );
response.on('error', console.error);
response.on('end', () => {
var dataObj = JSON.parse(data);
console.log("Retrieved response: " + JSON.stringify(dataObj.items));
const payload = {
"endpoints": []
};
dataObj.items.forEach(item => {
const device = {
"endpointId": item.id,
"manufacturerName": item.manufacturer,
"friendlyName": item.displayName,
"description": item.description,
"displayCategories": ["SWITCH"],
"cookie": {
"key1": "arbitrary key/value pairs for skill to reference this endpoint.",
"key2": "There can be multiple entries",
"key3": "but they should only be used for reference purposes.",
"key4": "This is not a suitable place to maintain current endpoint state."
},
"capabilities":
[
{
"type": "AlexaInterface",
"interface": "Alexa",
"version": "3"
},
{
"interface": "Alexa.PowerController",
"version": "3",
"type": "AlexaInterface",
"properties": {
"supported": [{
"name": "powerState"
}],
"retrievable": true
}
}
]
};
payload.endpoints.push(device);
});
console.log('payload ' + JSON.stringify(payload));
var header = request.directive.header;
header.name = "Discover.Response";
console.log("DEBUG", "Discovery Response: ", JSON.stringify({ header: header, payload: payload }));
//NEXT LINE IS EXECUTED WITHOUT ANY ERROR
context.succeed({ event: { header: header, payload: payload } });
});
});
req.on('error', (e) => {
console.log('problem with request: ' + e.message);
});
};
I found the problem...
The value of the property 'endpointId' contained a '#'. Then I changed the name to only letters, and it worked.
Although in this article it says '#' can be used, the discovery of devices then has problems.
Hope this answer helps others from wasting time...
I found another cause for the same symptom: for the entity's additionalAttributes (manufacturer, model etc.), one cannot use non-English characters. You can actually use any character ASCII from 32 to 126 (space to tilde), but you cannot use the backslash. So, no accent characters (international or extended ASCII) allowed.
On the other hand, I could include a entity with '#' inside its endpointId. I cannot explain why you couldn't.

Resources