Why is event.requestContext undefined for my AWS Lambda function? - node.js

Why was I unable to obtain the remote IP address of the user?
When I clicked on the test button or the function URL, it seems that event.requestContext was undefined. Why?
const res = {
statusCode: 200,
isBase64Encoded: false,
headers: {
"Access-Control-Allow-Origin":"*",
"Content-Type": "text/plain"
},
multiValueHeader: {}
};
export const handler = async(event) => {
const ip = event.requestContext.identity.sourceIp;
res.body="hi "+ip;
return res;
}

It seems that the event format is incorrect. This works:
const ip = event.requestContext.http.sourceIp;
What I used was the format for Lambda version 1 but I am now using version 2.

Related

LambdaInvalidResponse nodejs

I have the following code running as an AWS Lambda function:
const getDb = require('./db');
const Service = require('./service');
exports.main = async function (event, context) {
context.callbackWaitsForEmptyEventLoop = false;
const endpoint = event.path.split('/')[1].toLowerCase();
const service = new Service(
await getDb()
);
const result = await service[endpoint](); // result might be number or string, i.e. 123 or '123'
return {
isBase64Encoded: false,
statusCode: 200,
statusDescription: '200 OK',
headers: {
'Set-cookie': 'cookies',
'Content-Type': 'application/json',
},
body: result,
};
};
When I invoke it with aws-cli or the testing function in AWS console it works, but when I put it behind an ALB (application load balancer) it returns 502 Bad Gateway.
I have viewed the Lambda output CloudWatch logs. The endpoint is correctly invoked by the load balancer, but the load balancer access logs have the following entry:
2021-11-08T16:28:24.033000Z "forward" "-" "LambdaInvalidResponse" "-" "-" "-" "-"
Any ideas why I'm experiencing this issue?
Not sure which of these things fixed it, but I did fix it:
Wrap all properties in response object in quotes, i.e. { "body": result } instead of { body: result }
serialized result, i.e. { "body": JSON.stringify({ result }) } instead of { "body": result }
removed property statusDescription from response object

How to use variables as Node.js PUT request's json body

I'm new to programing and wanted to use the output from a child process as variable name, and this is how I failed:
var payload = outPutFromChildProcess;
{
var red = '{"sat": 254,"xy": [0.68,0.31]}';
var white = '{"ct":0}';
request({
method: 'PUT',
uri: 'http://192.168.2.17/api/*****',
json: JSON.stringify(payload)
}, (e, r, b) => {
if(e)return console.error("Bridge unreachable");
console.log(JSON.stringify(b[0].success));
});
}
I know it's very wrong on many level, and I'm deeply sorry for letting you read it.
But how do I make it work?
install axios: npm i axios
then:
const payload = { "sat": 254,"xy": [0.68,0.31] };
const config = { headers: {'Content-Type': 'application/json'} };
(async () => {
const res = await axios.put('http://192.168.2.17/api/*****', payload, config);
})();

PUT doesn't work on POSTMAN but okay when called from front-end

I have the following lambda function that works when called from the front-end React app:
// Update a contact
module.exports.updateContact = async (event, _context) => {
const id = event.pathParameters.id;
const body = JSON.parse(event.body);
const paramName = body.paramName;
const paramValue = body.paramValue;
const params = {
Key: {
id: id
},
TableName: contactsTable,
ConditionExpression: 'attribute_exists(id)',
UpdateExpression: 'set ' + paramName + ' = :v',
ExpressionAttributeValues: {
':v': paramValue,
},
ReturnValues: 'ALL_NEW'
};
try {
const res = await db.update(params).promise();
}
catch (err){
console.log(err);
return err;
}
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
status: 'updated!',
[paramName]: paramValue,
}),
};
return response;
};
But it fails with status code 502 & 'InternalServerErrorException' message when called from Postman.
I tried to solve it with one of the suggestions found on stackoverflow with method override:
It doesn't work and I'm now getting status code 403 and 'MissingAuthenticationTokenException'.
I'm not sure what I'm doing wrong, seeking some guidance. Thank you.
It seems the API endpoint has an Authorizer configured on the API Gateway and the front end is sending the correct credentials, while the Postman configuration is not.
I would suggest to use the browser dev tools selecting the PUT request and copy it as cUrl:
then you can paste the copied cUrl command into the Postman request. It will prefill all the parameters: method, body and the headers.
hope this help

Using images from Google Street View Static API without exposing key

I am trying to include Street View images on a React app. Here is the documentation for the API:
Street View Static API
When I make a simple fetch request to google I get a response that contains a URL to the street view image as a JPG file:
bodyUsed:false
headers:Headers
ok:true
redirected:false
size:0
status:200
statusText:"OK"
Symbol(Body internals):Object {body: PassThrough, disturbed: false, error: null}
Symbol(Response internals):Object {url: "https://maps.googleapis.com/maps/api/streetview?lo…", status: 200, statusText: "OK", …}
timeout:0
// URL BELOW HERE IS WHAT I'M USING
url:"https://maps.googleapis.com/maps/api/streetview?location=3737%20N%20Southport%20Ave,%20Chicago,%20IL%2060613&key={MY_SECRECT_KEY}&size=300x300"
I can't use this image URL directly in my app because it contains my secret API key.
What I'm attempting to do now is use an AWS lambda function that uses Node to read the image file from the URL and then send this image file as an HTTP response to the client's react app.
I'm having trouble figuring out how to do this in Node. I've seen some code online about using Node's readFile function from the file system module. But I'm having trouble getting it to work.
Here's the code in my Lambda function.:
const fetch = require('node-fetch');
const fs = require('fs');
const autoCompURL = "https://maps.googleapis.com/maps/api/streetview?"
const { G_PLACES_KEY } = process.env
const key = `&key=${G_PLACES_KEY}`
const size = "&size=300x300"
function getSearch(e) {
const resp = JSON.parse(e.body)
return resp.place_address
}
async function googleResults(str) {
const response = await fetch(
`${autoCompURL}location=${str}${key}${size}`
)
return new Promise(resolve => resolve(response));
}
exports.handler = async function(event, context) {
try {
const userSearch = getSearch(event)
const autoResults = await googleResults(userSearch)
const imgURL = autoResults.url
const img = await fs.promises.readFile(imgURL)
if (autoResults.status !== "OK") {
// NOT res.status >= 200 && res.status < 300
return { statusCode: autoResults.status, body: autoResults.statusText, error_message: autoResults.error_message }
}
return {
statusCode: 200,
headers: {'Content-Type': 'image/jpeg'},
body: img
}
} catch (err) {
console.log(err) // output to netlify function log
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }) // Could be a custom message or object i.e. JSON.stringify(err)
}
}
}
Appreciate any clues on how I can get this to work.
Did you try this option ?
You can generate Digital Signature and use it along with API invocation. And limit unsigned requests to zero .
https://cloud.google.com/blog/products/maps-platform/google-maps-platform-best-practices-securing-api-keys-when-using-static-maps-and-street-view-apis

Podio API addItem call

I'm trying to implement https://developers.podio.com/doc/items/add-new-item-22362 Podio API addItem call in a nodejs module. Here is the code:
var _makeRequest = function(type, url, params, cb) {
var headers = {};
if(_isAuthenticated) {
headers.Authorization = 'OAuth2 ' + _access_token ;
}
console.log(url,params);
_request({method: type, url: url, json: true, form: params, headers: headers},function (error, response, body) {
if(!error && response.statusCode == 200) {
cb.call(this,body);
} else {
console.log('Error occured while launching a request to Podio: ' + error + '; body: ' + JSON.stringify (body));
}
});
}
exports.addItem = function(app_id, field_values, cb) {
_makeRequest('POST', _baseUrl + "/item/app/" + app_id + '/',{fields: {'title': 'fgdsfgdsf'}},function(response) {
cb.call(this,response);
});
It returns the following error:
{"error_propagate":false,"error_parameters":{},"error_detail":null,"error_description":"No matching operation could be found. No body was given.","error":"not_found"}
Only "title" attribute is required in the app - I checked that in Podio GUI. I also tried to remove trailing slash from the url where I post to, then a similar error occurs, but with the URL not found message in the error description.
I'm going to setup a proxy to catch a raw request, but maybe someone just sees the error in the code?
Any help is appreciated.
Nevermind on this, I found a solution. The thing is that addItem call was my first "real"-API method implementation with JSON parameters in the body. The former calls were authentication and getApp which is GET and doesn't have any parameters.
The problem is that Podio supports POST key-value pairs for authentication, but doesn't support this for all the calls, and I was trying to utilize single _makeRequest() method for all the calls, both auth and real-API ones.
Looks like I need to implement one for auth and one for all API calls.
Anyway, if someone needs a working proof of concept for addItem call on node, here it is (assuming you've got an auth token beforehand)
_request({method: 'POST', url: "https://api.podio.com/item/app/" + app_id + '/', headers: headers, body: JSON.stringify({fields: {'title': 'gdfgdsfgds'}})},function(error, response, body) {
console.log(body);
});
You should set content-type to application/json
send the body as stringfied json.
const getHeaders = async () => {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
};
const token = "YOUR APP TOKEN HERE";
headers.Authorization = `Bearer ${token}`;
return headers;
}
const createItem = async (data) => {
const uri = `https://api.podio.com/item/app/${APP_ID}/`;
const payload = {
fields: {
[data.FIELD_ID]: [data.FIELD_VALUE],
},
};
const response = await fetch(uri, {
method: 'POST',
headers: await getHeaders(),
body: JSON.stringify(payload),
});
const newItem = await response.json();
return newItem;
}

Resources