Azure Cognitve Search upload document via browser cors issue - azure

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"
}
]
}

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.

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.

HTTP Post request not going through in AWS Lambda

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
}

Setting Cookie in http response header from AWS lambda Node JS

I have a Lambda proxy integration enabled, and setting the response headers as part of Lambda output and API Gateway that will return them as part of the HTTP response to the client.
Sample code:
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "headerName": "headerValue", ... },
"body": "..."
});
I need to send out 3 cookies in the headers. I tried. But, failed:
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": [cookie1String, cookie2String, cookie3String] },
"body": "..."
});
[Edit]
I concatenated the cookie and passed in as the response, the client gets the cookie. But when the client calls the target in "location", the request does not have the cookie in the header.
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": c1=cookie1String;c2=cookie2String; c3=cookie3String] },
"body": "..."
});
Please help in sending these 3 cookies out to my client.
Use multiValueHeaders instead of headers.
const response = {
isBase64Encoded: true,
statusCode: 200,
multiValueHeaders : {"Set-Cookie": [`language=${language}`, `theme=${theme}`]},
body: JSON.stringify('User profile set successfully')
};
callback(null, response);
If you need it to be smarter, consider something like
function createHeaders(headers) {
const defaultHeaders = {
'Access-Control-Allow-Origin': '*',
};
const allHeaders = Object.assign({}, defaultHeaders, headers);
const singleValueHeaders = {};
const multiValueHeaders = {};
Object.entries(allHeaders).forEach(([key, value]) => {
const targetHeaders = Array.isArray(value) ? multiValueHeaders : singleValueHeaders;
Object.assign(targetHeaders, { [key]: value });
});
return {
headers: singleValueHeaders,
multiValueHeaders,
};
}
Then use it in the callback function.
callback(null, {
statusCode: status || 200,
body: JSON.stringify(body),
...createHeaders({ 'Set-Cookie': cookie }),
});
API gateway does not let you map the same header more than once. I got around by using different casing to set-cookie method.
callback(null, {
"statusCode": 302,
"Location" : "https://somewebsite.com"
"headers": { "Set-Cookie": cookie1, "set-Cookie": cookie2 },
"body": "..."
});
I would say that your issue is related to the fact that your response object in the callback is not formatted the way the api gateway expects.
These links reference aws documentation specifically to that.
http://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html
Issue with your code...
'location' does not look like a valid property
Make sure your header key/value pairs are actual JSON objects using something like JSON.stringify
Don't forget to enable logs for both api gateway and lambda with full requests and responses. These two logs will help you debug.

NodeJS eventloop execution order

When sending several requests serially, it seems like
callbacks are executed when all requests are sent.
requests seems to get added into a queue but not actually getting executed before the loop is done.
Example
var http = require('http');
var i=0;
var postData = [];
while (i++ < 12) {
var largeObject = [
'this' + i,
'that' + i,
'then' + i
];
postData.push(largeObject);
if (postData.length >= 2) {
console.log('request');
var options = {
hostname: 'httpbin.org',
port: 80,
path: '/post',
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
};
var req = http.request(options, function(res) {
console.log(res.statusCode);
res.on('data', function(chunk) {
console.log(chunk.toString());
});
res.on('end', function() {
console.log('finished reading response');
});
});
req.end(JSON.stringify(postData), function() {
console.log('request stream finished');
});
postData = [];
}
}
The output here would look like
request
request
request
request
request
request
request stream finished
request stream finished
request stream finished
request stream finished
request stream finished
request stream finished
200
{
"args": {},
"data": "[[\"this7\",\"that7\",\"then7\"],[\"this8\",\"that8\",\"then8\"]]",
"files": {},
"form": {},
"headers": {
"Content-Length": "53",
"Content-Type": "application/json",
"Host": "httpbin.org"
},
"json": [
[
"this7",
"that7",
"then7"
],
[
"this8",
"that8",
"then8"
]
],
"origin": "xxxxx",
"url": "http://httpbin.org/post"
}
finished reading response
200
{
"args": {},
"data": "[[\"this1\",\"that1\",\"then1\"],[\"this2\",\"that2\",\"then2\"]]",
"files": {},
"form": {},
"headers": {
"Content-Length": "53",
"Content-Type": "application/json",
"Host": "httpbin.org"
},
"json": [
[
"this1",
"that1",
"then1"
],
[
"this2",
"that2",
"then2"
]
],
"origin": "xxxx",
"url": "http://httpbin.org/post"
}
...
Is there any way to finish one request before the next one is getting executed?
I don't really care about the order, it's more about memory - I'd like to get rid of large objects I am posting
e.g.
request1
response1
request2
response2
request3
response3
request4
request5
response5
response4
would be absolutely fine.
Any suggestion?
Of course, just use some control-flow modules like async or bluebird.
As you don't care about order, I'd advise you to use either async#parallel or bluebird#map. The bluebird#map has a concurrency argument, which can be nice when you need some more control over loops and their amount (so does async#parallelLimit).
If this doesn't seem straightforward, please comment and I'll add an example.

Resources