RunTime.IMportModuleError "AWS lambdas" - amazon-web-services

for some reason when doing "sam deploy" and "sam local start-api" I am getting this error that I have not been able to solve.
Does anyone know what is going on ?
My Lamdba function:
const Responses = require('../helpers/Response')
exports.handler = async( event, context, callback ) => {
try{
const userInputs = JSON.parse( event.body );
return Responses._200({ msg: 'Hello world' })
}catch(err){
console.log( err )
return callback( null, { msg: 'Algo saliĆ³ mal | leer logs' }
);
};
};
The module I try to import:
const { HttpStatusCode } = require("./HttpStatusCode")
const headers = {
'Content-type': 'application/json',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Origin': '*'
}
const Responses = {
_200( data = {} ){
return{
headers,
statusCode: HttpStatusCode.OK,
body: JSON.stringify( data )
}
}
};
module.exports = Responses
The Output:
Invalid lambda response received: Invalid API Gateway Response Keys: {'errorMessage', 'trace', 'errorType'} in {'errorType': 'Runtime.ImportModuleError', 'errorMessage': "Error: Cannot find module '../helpers/Response'\nRequire stack:\n- /var/task/index.js\n- /var/runtime/index.mjs", 'trace': ["Runtime.ImportModuleError: Error: Cannot find module '../helpers/Response'", 'Require stack:', '- /var/task/index.js', '- /var/runtime/index.mjs', ' at _loadUserApp (file:///var/runtime/index.mjs:1000:17)', ' at async Object.UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1035:21)', ' at async start (file:///var/runtime/index.mjs:1200:23)', ' at async file:///var/runtime/index.mjs:1206:1']}
I have already tried to do all possible imports, but that doesn't solve anything.

Related

Lambda function with UpdateRestApiCommand, how to update to work in version node 18

I updated a lambda function to node 18, but there are changes to do with my UpdateRestApiCommand.
Here's the original that worked in the older version:
const request = apigateway.updateRestApi(params);
request
.on('success', function(response) {
console.log("Success!");
resolve(response.data);
}).
on('error', function(error, response) {
console.log("Error!");
reject(response.error);
}).
on('complete', function(response) {
console.log("Done!");
})
.send()
});
Here's my imports:
const https = require("https");
const env = process.env.ENV;
const resource = process.env.RESOURCE;
const restApiId = process.env.REST_API_ID;
const ce_base_url = process.env.CE_BASE_URL;
const { APIGatewayClient, UpdateRestApiCommand } = require("#aws-sdk/client-api-gateway");
const stage = process.env.STAGE;
And now I've found I need to use UpdateRestApiCommand I think so I've got this:
new UpdateRestApiCommand(params)
.on('success', function(response) {
console.log("Success!");
resolve(response.data);
}).
on('error', function(error, response) {
console.log("Error!");
reject(response.error);
}).
on('complete', function(response) {
console.log("Done!");
})
.send()
});
Here's the error I'm getting:
ERROR Invoke Error
{
"errorType": "TypeError",
"errorMessage": "(intermediate value).on is not a function",
"stack": [
"TypeError: (intermediate value).on is not a function",
" at /var/task/index.js:64:8",
" at new Promise (<anonymous>)",
" at exports.handler (/var/task/index.js:36:25)",
" at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"
]
}
The nodejs18.x runtime has the AWS JS SDK v3 pre-installed. Earlier runtime versions came with v2.
Here's the basic pattern to use the v3 SDK clients in a Lambda handler:
const client = new APIGatewayClient({
region: process.env.AWS_REGION,
});
export async function handler(event, context) {
// ...
const cmd = new UpdateRestApiCommand(params);
const response = await client.send(cmd);
}

Twitter_api_v2 reply to a tweet example

Could anyone Show me an example of your finished Parameters and Endpoint for a Twitter Reply maybe with a Screenshot? Because i dont understand exactly what to type in my Params and, do I got to change anything in the Pre-request Script?
Kind regards Alex
For the Params for https://api.twitter.com/2/tweets I tried:
Key : in_reply_to
Value : tweet_id
And the Result was "errors"
"message": "The query Parameters [in_reply_to] is not one of [expantions,tweet.fields,media.fields,poll.fields,place.fields,user.fields]"
"title":"Invalid Request"
"detail": "One or more Parameters to your request Was invalid.",
"type":"https://api.twitter.com/2/problems/invalid-request"
From the twitter's documentation
query parameter ids is required. You missed that parameter.
I will get tweet this demo
https://twitter.com/pascal_bornet/status/1604754709000200193
BY Postman
Full code by node.js
#1 Get an access token by API Key and API secret
#2 Get text by access token
Credential in config.json
{
"API_KEY" : "7hK your API Key GND",
"API_KEY_SECRET" : "Zr4 your API Key secret 0qX0"
}
Save as get-tweet.js
const axios = require('axios')
const config = require('./config.json');
const getAccessToken = async () => {
try {
const resp = await axios.post(
'https://api.twitter.com/oauth2/token',
'',
{
params: {
'grant_type': 'client_credentials'
},
auth: {
username: config.API_KEY,
password: config.API_KEY_SECRET
}
}
);
// console.log(resp.data);
return Promise.resolve(resp.data.access_token);
} catch (err) {
// Handle Error Here
console.error(err);
return Promise.reject(err);
}
};
const getTweetText = async (token, tweet_id) => {
try {
const resp = await axios.get(
`https://api.twitter.com/2/tweets?ids=${tweet_id}`,
{
headers: {
'Authorization': 'Bearer '+ token,
}
}
);
return Promise.resolve(resp.data);
} catch (err) {
// Handle Error Here
console.error(err);
return Promise.reject(err);
}
};
getAccessToken()
.then((token) => {
console.log(token);
getTweetText(token, '1604754709000200193')
.then((result) => {
console.log(result.data[0].text);
})
})
Get Result
$ node get-tweet.js
AAAAAksadf--very long access token in here ----JlIMJIIse
Is this the future of Christmas shopping?
Credit: Nike
#innovation #AR # VR #AugmentedReality https://~~~

Modify Cloudfront origin response with Lambda - read-only headers

I have a Cloudfront distribution with a single React site, which is hosting in S3. The origin is connected via REST api. To properly handle queries, I use custom error responses on status 403 and 404 to 200 and route them to root. The root object is index.html and everything seems to be fine.
Now I have a task to add to a distribution an another site, which should be accessible through a subdirectory.
To do this I have to set a root object for a subdirectory and to catch 404 and 403 responses and transfer them to a root object. I've already set up origin and behaviour.
I tried to use theese manuals:
example
source
but it seems that something went wrong
The first approach (CloudFrontSubdirectoryIndex) seems not working at all (the function is not invoked and no rewrite happens), so i tried CloudFront function and it seems to work fine.
The last step is to handle 404 and 403 responses.
Here is the function from the manual:
'use strict';
const http = require('https');
const indexPage = 'index.html';
exports.handler = async (event, context, callback) => {
const cf = event.Records[0].cf;
const request = cf.request;
const response = cf.response;
const statusCode = response.status;
// Only replace 403 and 404 requests typically received
// when loading a page for a SPA that uses client-side routing
const doReplace = request.method === 'GET'
&& (statusCode == '403' || statusCode == '404');
const result = doReplace
? await generateResponseAndLog(cf, request, indexPage)
: response;
callback(null, result);
};
async function generateResponseAndLog(cf, request, indexPage){
const domain = cf.config.distributionDomainName;
const appPath = getAppPath(request.uri);
const indexPath = `/${appPath}/${indexPage}`;
const response = await generateResponse(domain, indexPath);
console.log('response: ' + JSON.stringify(response));
return response;
}
async function generateResponse(domain, path){
try {
// Load HTML index from the CloudFront cache
const s3Response = await httpGet({ hostname: domain, path: path });
const headers = s3Response.headers ||
{
'content-type': [{ value: 'text/html;charset=UTF-8' }]
};
return {
status: '200',
headers: wrapAndFilterHeaders(headers),
body: s3Response.body
};
} catch (error) {
return {
status: '500',
headers:{
'content-type': [{ value: 'text/plain' }]
},
body: 'An error occurred loading the page'
};
}
}
function httpGet(params) {
return new Promise((resolve, reject) => {
http.get(params, (resp) => {
console.log(`Fetching ${params.hostname}${params.path}, status code : ${resp.statusCode}`);
let result = {
headers: resp.headers,
body: ''
};
resp.on('data', (chunk) => { result.body += chunk; });
resp.on('end', () => { resolve(result); });
}).on('error', (err) => {
console.log(`Couldn't fetch ${params.hostname}${params.path} : ${err.message}`);
reject(err, null);
});
});
}
// Get the app path segment e.g. candidates.app, employers.client etc
function getAppPath(path){
if(!path){
return '';
}
if(path[0] === '/'){
path = path.slice(1);
}
const segments = path.split('/');
// will always have at least one segment (may be empty)
return segments[0];
}
// Cloudfront requires header values to be wrapped in an array
function wrapAndFilterHeaders(headers){
const allowedHeaders = [
'content-type',
'content-length',
'last-modified',
'date',
'etag'
];
const responseHeaders = {};
if(!headers){
return responseHeaders;
}
for(var propName in headers) {
// only include allowed headers
if(allowedHeaders.includes(propName.toLowerCase())){
var header = headers[propName];
if (Array.isArray(header)){
// assume already 'wrapped' format
responseHeaders[propName] = header;
} else {
// fix to required format
responseHeaders[propName] = [{ value: header }];
}
}
}
return responseHeaders;
}
When i try to implement this solution (attach the function to origin response) I get
The Lambda function result failed validation: The function tried to add, delete, or change a read-only header.
Here is a list of restricted headers, but I'm not modifying any of them.
If I try not to attach any headers to a response at all, the message is the same.
If I try to attach all headers, CloudFront says that i'm modifying a black-listed header.
Objects in a bucket have only one customized Cache-Control: no-cache metadata.
It seemed to be a fast task, but I'm stuck for two days already.
Any help will be appreciated.
UPD: I've searched the logs and found
ERROR Validation error: Lambda function result failed validation, the function tried to delete read-only header, headerName : Transfer-Encoding.
I'm a little bit confused. This header is not present in origin response, but CF is telling that I deleted it...
I tried to find the value of the header "Transfer-Encoding" that should come from origin (S3) but it seems that it has been disappeared. And CloudFront says that this header is essential.
So I've just hard-coded it and everything becomes fine.
'use strict';
const http = require('https');
const indexPage = 'index.html';
exports.handler = async (event, context, callback) => {
const cf = event.Records[0].cf;
const request = cf.request;
const response = cf.response;
const statusCode = response.status;
// Only replace 403 and 404 requests typically received
// when loading a page for a SPA that uses client-side routing
const doReplace = request.method === 'GET'
&& (statusCode == '403' || statusCode == '404');
const result = doReplace
? await generateResponseAndLog(cf, request, indexPage)
: response;
callback(null, result);
};
async function generateResponseAndLog(cf, request, indexPage){
const domain = cf.config.distributionDomainName;
const appPath = getAppPath(request.uri);
const indexPath = `/${appPath}/${indexPage}`;
const response = await generateResponse(domain, indexPath);
console.log('response: ' + JSON.stringify(response));
return response;
}
async function generateResponse(domain, path){
try {
// Load HTML index from the CloudFront cache
const s3Response = await httpGet({ hostname: domain, path: path });
const headers = s3Response.headers ||
{
'content-type': [{ value: 'text/html;charset=UTF-8' }]
};
s3Response.headers['transfer-encoding'] = 'chunked';
return {
status: '200',
headers: wrapAndFilterHeaders(headers),
body: s3Response.body
};
} catch (error) {
return {
status: '500',
headers:{
'content-type': [{ value: 'text/plain' }]
},
body: 'An error occurred loading the page'
};
}
}
function httpGet(params) {
return new Promise((resolve, reject) => {
http.get(params, (resp) => {
console.log(`Fetching ${params.hostname}${params.path}, status code : ${resp.statusCode}`);
let result = {
headers: resp.headers,
body: ''
};
resp.on('data', (chunk) => { result.body += chunk; });
resp.on('end', () => { resolve(result); });
}).on('error', (err) => {
console.log(`Couldn't fetch ${params.hostname}${params.path} : ${err.message}`);
reject(err, null);
});
});
}
// Get the app path segment e.g. candidates.app, employers.client etc
function getAppPath(path){
if(!path){
return '';
}
if(path[0] === '/'){
path = path.slice(1);
}
const segments = path.split('/');
// will always have at least one segment (may be empty)
return segments[0];
}
// Cloudfront requires header values to be wrapped in an array
function wrapAndFilterHeaders(headers){
const allowedHeaders = [
'content-type',
'content-length',
'content-encoding',
'transfer-encoding',
'last-modified',
'date',
'etag'
];
const responseHeaders = {};
if(!headers){
return responseHeaders;
}
for(var propName in headers) {
// only include allowed headers
if(allowedHeaders.includes(propName.toLowerCase())){
var header = headers[propName];
if (Array.isArray(header)){
// assume already 'wrapped' format
responseHeaders[propName] = header;
} else {
// fix to required format
responseHeaders[propName] = [{ value: header }];
}
}
}
return responseHeaders;
}

AWS Lambda - unable to convert SDK call to promise

I have a Lambda which looks like so:
module.exports.handler = (event, context, callback) => {
AWS.config.setPromisesDependency(null);
const uploadPromise = s3.upload(params).promise();
uploadPromise.then((data) => {
const response = {
...
};
return response;
}).catch((error) => {
console.log(error);
});
};
Calling it from Postman results in server error in Postman. CloudWatch logs have no further info.
Doing:
s3.upload(params, (error, data) => {
if (error) {
console.error("error occurred storing to s3: ", error);
return;
}
const response = {
...
};
return response;
});
does not result in a server error.
I am trying to follow the information from AWS that can be found here:
https://aws.amazon.com/blogs/developer/support-for-promises-in-the-sdk/
Postman is able to upload to Lambda by doing the following with async/await and try/catch:
exports.handler = async function(event, context) {
const s3 = new AWS.S3();
const encodedImage = util.inspect(event.body);
const decodedImage = Buffer.from(encodedImage, "base64");
const filePath = "test.png";
const params = {
Body: decodedImage,
Bucket: "my bucket",
Key: filePath,
ACL: "public-read",
ContentType: "mime/png"
};
try {
const result = await s3.upload(params).promise();
const response = {
statusCode: 200,
headers: {
my_header: "my_value"
},
body: JSON.stringify(result),
isBase64Encoded: false
};
return response;
} catch (error) {
console.log('error')
}
};

Using AWS Services ( like IOT ) in regions without cognito

I am looking into using AWS IOT to let our hardwares communicate with user phones. We are using react-native-paho-mqtt library for this purpose.
I shall explain in short before the code:
retrieve token from cognito federated identity in ap-northeast-1 ( tokyo ) , because cognito / federated identity is available there
use that token to log into aws IOT in ap-southeast-1 ( singapore )
fails with : Error: AMQJS0007E Socket error: Unknown socket error.
the generated mqtt endpoint with sigV4 signing looks like this :
wss://a2mt3pd9aiue3c.iot.ap-southeast-1.amazonaws.com/mqtt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAIPXKPMB3YECA4IWQ/20170730/ap-northeast-1/iotdevicegateway/aws4_request&X-Amz-Date=20170730T132924Z&X-Amz-SignedHeaders=host&X-Amz-Signature=b67e19aa6a16703756b0fed6f60649e687ca2494ad23e0b28e0d6ba624b53fdb&X-Amz-Security-Token=yyy//////////bbb/xxx+PV2/YEn3Li8b/T7+hVm6v5HJBu6fSqiMFk8ReLIJYqRKb6dCVvKjow3ioGIVtTMs5dABg9rHmSH1Q4y1RZaje4pNjTakTbgb2uhnQ4AjNrlgb75WQxsckXEDr7QKy5+lWWO6C/tU3+kymk8KCBYBq1yoq5GOBlc9BdajwsrLOdDLozmIsaarqlSMQ/iS/SOoNmx7AK733hxUEU5ovaXKY5ffLRbJm+JqVxI+0zJP4Q4fHGX4f2qkQBUCzmLl454fKanmGk3yXoO0FH2jlcztoqGirvBAiP//////////8BEAAaDDUzODEwNjcxOTUxNSIMoUem00KSLGQAkZRxKsME5hnmdjdZVMQHgcc+gHRFraUqOwUJ0l4g/rUMzQVythsplcDOQ1gqFtAUiNm4Oy520ubgJylOHBSmzc+Zqjyd42Mh2a5kF1vj0FxwPpVJrzxEV1yRS6nLs6D6XMAaKZPrz0zMDriKI77LKyyBwtSBlI9ENILzYrGwe+i5K04TyYRJSZBszOZXtCVwDYQUJTwMzfNWY4R0UhyjC9/xxx+P/lWwxxA4FVVlWcc+xV8Fwjpem4/yl6eH0YmHhPrAAvQDAq+euBlbDJL5aV8czUQulowEJum6oVocngqP2W2zwid1fJT2C3xnVySZEHLrUZ7aULD6VswvzmKqmE9RBPE3+lPgsOKtGzw/BtdQYQmHsggqA8C7omwAzwUNAqa4/uVYGNdXUDUxytq9J2qFUeh74o0c/QsyUMcgsK/3hJySiMj+M7TuPTTAhCnf5WS3wL9dMjLsis88wGFbkUnOVS0N3hpFGLoIpc/EWy7hnbYZDJiLG9B02SiLtshZBc6D+somethingsomething+gVd7jdk0K6wMFwJ3Xc4tFoDzps13eszpMEfrMGGQSdKK7dFfwfMLO+98sF
the code fragment is this:
fetch('https://xxx.execute-api.ap-northeast-1.amazonaws.com/dev/app/connect', { // retrieve app token from ap-northeast-1 cognito
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({"data": {"coreUserId": 100, "companyId": 1,
"brandId": 2, "hotelId": 3, "roomId": 4}})
})
.then((response) => response.json())
.then((responseJson) => {
console.warn("resnpose json is : ", responseJson)
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AppConfig.identityPoolId,
IdentityId: responseJson.message.cognitoIdentityId,
Logins: {
'cognito-identity.amazonaws.com': responseJson.message.token
}
});
AWS.config.credentials.get((err) => {
if (err) {
console.error(err)
}
console.warn("AWS.config.credentials --> ", AWS.config.credentials)
var ioturl = this.getSignedUrl(AppConfig.iotEndpoint, AppConfig.region, AWS.config.credentials);
const client = new Client({
uri: ioturl,
clientId: AppConfig.credentials.cognitoIdentityId,
storage: AsyncStorage
});
this.client = client;
client.on('messageReceived', (message) => {
try {
const jsonResponse = JSON.parse(message.payloadString);
console.warn(Date.now()," : received : ", jsonResponse);
} catch (e) {
console.warn("Failed to recieve: ", e)
}
//this.handleIoTMessage(jsonResponse);
});
client.on('connectionLost', (responseObject) => {
console.warn('CLIENT DC');
if (responseObject.errorCode !== 0) {
//clearInterval(this.publishInterval);
console.warn('CONNECTIONLOST TRIGGERED:', responseObject.errorMessage);
}
});
var connectOptions = {
useSSL: true,
timeout: 30000, // In milliseconds, it turns out
keepAliveInterval:30000,
cleanSession:true,
mqttVersion: 4,
};
AWS.config.update({region: "ap-southeast-1"}); //now try to connect to another region..where there is no cognito available
const IoT = new AWS.Iot();
var params = {
policyName: "Test",
principal: AppConfig.credentials.cognitoIdentityId // this cognito identity id is in ap-northeast-1 . NOT in ap-southeast-1 where IOT is located
};
IoT.attachPrincipalPolicy(params, (err) => {
if (err) {
console.error('ERROR attachPrincipalPolicy: ', err);
}
client.connect(connectOptions)
.then((result) => {
console.warn("connect resutl : ", result, " .. not subs ")
return client.subscribe('b/2/p/3/r/4/#', {qos: 1, timeout: 15000})
})
.then(function (xx, yy) {
console.warn("Subscribe succss : ", xx, yy)
const message = new Message(JSON.stringify({"xxx": "yyy"}));
message.destinationName = 'b/2/p/3/r/4/xx';
return client.send(message);
})
.then(function (xx, yy) {
return console.warn("Successfully sent : ", xx, yy)
})
.catch((responseObject) => {
console.warn("response object is : ", responseObject);
if (responseObject.errorCode !== 0) { // fail
console.warn(`CATCH ONCONNECTIONLOSTTRIGGERED: ${responseObject.errorMessage}`);
}
});
});
});
})
.catch((error) => {
console.error(error);
});
And it fails with :
Error: AMQJS0007E Socket error: Unknown socket error.
Can anyone suggest what needs to be done? Also what is the standard for authenticating users in regions without cognito ?
Maybe you are aware of this already but Cognito launched in Singapore recently