AWS S3 promise meteor - amazon-web-services

So, I'm using AWS = Npm.require('aws-sdk'); from Amazon.
Apparently promise are implemented. http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html#promise-property
See my functions (Server):
s3 = new AWS.S3({ apiVersion: "2006-03-01" });
bucketExists = (bucket) => {
check(bucket, String);
const params = {
Bucket: bucket
};
return s3.putObject({Bucket: 'bucket', Key: 'key'}).promise();
};
Meteor.methods({
addPhoto(userId, photo) {
check(userId, String);
check(photo, Object);
bucketExists(userId)
.then(
function(value) {
console.log("Contents: " + value);
})
.catch(function(reason) {
console.error("Error or timeout", reason);
});
}
});
Exeption:
Exception while invoking method 'addPhoto' TypeError: Object [object Object] has no method 'promise'.
WTF
So, I try without call promise(). Like that:
bucketExists = (bucket) => {
check(bucket, String);
const params = {
Bucket: bucket
};
return new Promise(
(resolve, reject) => {
s3.waitFor('bucketExists', params, (err, res) => {
if (err) reject(err);
else resolve(res);
});
});
};
But nothing appear. I start with ES6. So please help.
Thanks

Related

Lambda failed silently without any logs, when updating a record from dynamodb table

I am using a lambda function for dynamo streaming after a record is inserted.
However, after the dynamoDB.update call, it seems the lambda is dead, and there are no other logs printed. Can anyone help?
Here is my javascript code:
/* Amplify Params - DO NOT EDIT
API_SCHOLARGRAPH_EMPLOYEEINFOTABLE_ARN
API_SCHOLARGRAPH_EMPLOYEEINFOTABLE_NAME
ENV
REGION
Amplify Params - DO NOT EDIT */
const AWS = require('aws-sdk');
const awsConfig = {
region: process.env.REGION,
endpoint:
process.env.DYNAMODB_ENDPOINT ||
`https://dynamodb.${process.env.REGION}.amazonaws.com`
};
const dynamoDB = new AWS.DynamoDB.DocumentClient(awsConfig);
async function handleNewEmployeeInfo(dynamoId, employeeId) {
console.log(
`[handleNewEmployeeInfo] begin. dynamoId(${dynamoId}) employeeId(${employeeId})`
);
try {
const employeeName = "TestString";
// this log is working
console.log(
'try to update table:',
process.env.API_SCHOLARGRAPH_EMPLOYEEINFOTABLE_NAME,
dynamoId,
employeeName
);
// something wrong with the update
await dynamoDB
.update({
TableName: process.env.API_SCHOLARGRAPH_EMPLOYEEINFOTABLE_NAME,
Key: {
id: dynamoId
},
UpdateExpression: `set employeeName = :employeeName`,
ExpressionAttributeValues: {
':employeeName': employeeName
}
})
.promise()
.then((data) => console.log(data.Attributes))
.catch(console.error);
} catch (error) {
// NOT working
console.log('[ERROR]:', error);
}
// NOT working
console.log('[FINISH] call end');
}
async function handleEventRecord(record) {
console.log(record.eventID);
console.log(record.eventName);
try {
// after EmployeeInfo is created by the admin
if (record.eventName === 'INSERT') {
const arn = record.eventSourceARN;
console.log(`[INSERT][${arn}]: ${JSON.stringify(record)}`);
if (arn.includes(':table/EmployeeInfo')) {
const dynamoId = record.dynamodb.NewImage.id['S'];
const employeeId = record.dynamodb.NewImage.employeeId['S'];
await handleNewEmployeeInfo(dynamoId, employeeId);
}
}
} catch (error) {
console.log('[ERROR]:', error);
}
}
/**
* #type {import('#types/aws-lambda').APIGatewayProxyHandler}
*/
exports.handler = async (event) => {
console.log(`EVENT: ${JSON.stringify(event)}`);
event.Records.forEach(async (record) => {
await handleEventRecord(record);
});
return Promise.resolve('Successfully processed DynamoDB record');
};
After I remove the update logic, the following log works fine. So I am pretty sure it is the update line has the problem.

Migrate GetObject (v2) to GetObjectCommand (v3) - aws-sdk

I'm trying to migrate an Express endpoint from v2 to v3 of the aws-sdk for JavaScript. The endpoint is a file downloader for AWS S3.
In version 2, I passed the result of GetObject back to the browser in a readable stream. In version 3 that same technique fails with the error:
TypeError: data.Body.createReadStream is not a function
How do I work with the data that returned from the new GetObjectCommand? Is it a blob? I'm struggling to find anything useful in the v3 SDK docs.
Here are the two versions of the endpoint:
import AWS from 'aws-sdk'
import dotenv from 'dotenv'
import { GetObjectCommand, S3Client } from '#aws-sdk/client-s3'
dotenv.config()
// VERSION 3 DOWNLOADER - FAILS
const getFileFromS3v3 = async (req, res) => {
const client = new S3Client({ region: 'us-west-2' })
const params = {
Bucket: process.env.AWS_BUCKET,
Key: 'Tired.pdf',
}
const command = new GetObjectCommand(params)
try {
const data = await client.send(command)
console.log(data)
data.Body.createReadStream().pipe(res)
} catch (error) {
console.log(error)
}
}
// VERSION 2 DOWNLOADER - WORKS
const getFileFromS3 = async (req, res) => {
const filename = req.query.filename
var s3 = new AWS.S3()
var s3Params = {
Bucket: process.env.AWS_BUCKET,
Key: 'Tired.pdf',
}
// if the file header exists, stream the file to the response
s3.headObject(s3Params, (err) => {
if (err && err.code === 'NotFound') {
console.log('File not found: ' + filename)
} else {
s3.getObject(s3Params).createReadStream().pipe(res)
}
})
}
export { getFileFromS3, getFileFromS3v3 }
This version 3 code works. Thanks to a major assist, the trick was to pipe data.Body and not use any of the fileStream methods.
import { GetObjectCommand, S3Client } from '#aws-sdk/client-s3'
import dotenv from 'dotenv'
dotenv.config()
const getFileFromS3 = async (req, res) => {
const key = req.query.filename
const client = new S3Client({ region: process.env.AWS_REGION })
const params = {
Bucket: process.env.AWS_BUCKET,
Key: key,
}
const command = new GetObjectCommand(params)
try {
const data = await client.send(command)
data.Body.pipe(res)
} catch (error) {
console.log(error)
}
}
export { getFileFromS3 }
When called from this frontend function the code above returns the file from S3 to the browser.
const downloadFile = async (filename) => {
const options = {
url: `/api/documents/?filename=${filename}`,
method: 'get',
responseType: 'blob',
}
try {
const res = await axios(options)
fileDownload(res.data, filename)
} catch (error) {
console.log(error)
}
}

How to scan the dynamodb table form the AWS Lambda function

I am trying to scan the Dynamodb table form my following code, can anyone please guide me what is wrong here.
const AWS = require("aws-sdk");
const dynamodb = new AWS.DynamoDB({
region: "eu-west-1",
apiVersion: "2012-08-10"
});
exports.handler = async (event, callback) => {
const params = {
TableName: "job_Status"
};
dynamodb.scan(params, (err, data) => {
if (err) {
console.log(err);
callback(err);
} else {
console.log(data);
callback(null, data);
}
});
};
I have given full dynamodb access role to the function but still it gives me the null response. Any idea what can be wrong here?
Response:
I tried with dynaomClient which not working too.
const AWS = require("aws-sdk");
const db = new AWS.DynamoDB.DocumentClient({
region : 'eu-west-1'
});
exports.handler = async (event, callback) => {
const params = {
TableName: "job_Status"
};
db.scan(params, (err, data) => {
if (err) {
console.log(err);
callback(err);
} else {
console.log(data);
callback(null, data);
}
});
};
Your Lambda function is async but your code uses callbacks. By the time the callback is reached, your function has already been terminated because it ran asynchronously. I'd speculate that the null output you see is the return value from the Lambda function, not your console.log.
Replace your call to scan with the following:
try{
let results = await db.scan(params).promise()
console.log(results);
} catch(err){
console.log(err)
}
For more info, check out the AWS documentation about working with promises.

Generate S3 URL in "virtual-hosted style" format

I'm using Javascript SDK on AWS Lambda to generate signed URL. But getSignedUrl is only returning path style URL. I have tried setting s3ForcePathStyle as false.
const params = {
Bucket: 'bucket_name',
Key: 'key_name'
};
const options = {
signatureVersion: 'v4',
useAccelerateEndpoint: false,
// endpoint: new AWS.Endpoint('https://bucket_name.s3.amazonaws.com'),
s3ForcePathStyle: false
};
const client = new AWS.S3(options);
exports.handler = async (event) => {
const signedURL = await (new Promise((resolve, reject) => {
client.getSignedUrl('putObject', params, (err, data) => {
if (err) {
reject(err);
}
else {
resolve(data);
}
});
}));
return signedURL;
};
If I uncomment endpoint: new AWS.Endpoint('https://bucket_name.s3.amazonaws.com') I'm getting an absurd URL which combines both:
https://bucket_name.s3.amazonaws.com/bucket_name/key_name......
tl;dr
https://s3.ap-south-1.amazonaws.com/bucket_name/key_name..... # Code returns path-style
https://bucket_name.s3.amazonaws.com/key_name..... # I want virtual-hosted-style

How to access the aws parameter store from a lambda using node.js and aws-sdk

I've created a lambda and cloud formation template which grants a lambda access to the parameter store and secrets manager. When I test the lambda I have the following functions outside of the export.handler function:
function getParameterFromStore(param){
let promise = new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
let parameterResult = promise.then(function(result){
console.log('---- result: '+ JSON.stringify(result));
return result;
});
return parameterResult;
};
servmgr is instantiated as var servmgr = new AWS.SSM();
When I call this function from the export.handler function I do so as:
myFirstParam = { Path : '/myPath/Service/servicesEndpoint'};
let endpointResult = getParameterFromStore(myFirstParam);
In the lambda I have the function retrieve the parameter defined outside of the export.handler function bt wrapped in a promise.
When I run/test this lambda the object returned is always undefined... I get Parameters[] back but no values.
2019-02-20T21:42:41.340Z 2684fe88-d552-4560-a477-6761f2de6717 ++ /myPath/Service/serviceEndpoint
2019-02-20T21:42:41.452Z 2684fe88-d552-4560-a477-6761f2de6717 ---- result: {"Parameters":[]}
How do you get parameter values returned back to a lambda at run time?
update
based upon the suggestion/answer from Thales I've simplified the lambda to just this:
const getParameterFromStoreAsync = (param) => {
return new Promise((resolve, reject) => {
servmgr.getParametersByPath(param, (err, data) => {
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
}
return resolve(data);
});
});
};
exports.handler = async(event, ctx, callback) => {
console.log('INFO[lambda]: Event: [' + JSON.stringify(event, null, 2) + ']');
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));
done = ()=>{}
callback(null, done());
};
I am still seeing an empty array being returned in my tests:
### endpoint path: {"Parameters":[]}
I've also moved the function into the callback as
exports.handler = (event,ctx, callback){
done = async()=>{
console.log('this is the event' + JSON.stringify(event));
sfdcEndPointParam = { Path : '/PartnerBanking/Service/SfdcEndpoint'};
let myendpoint = await getParameterFromStoreAsync(sfdcEndPointParam);
console.log('### endpoint path: ' + JSON.stringify(myendpoint));}
}
callback(null, done());
Same result ... empty array. Any additional things to try?
This is because your getParameterFromStore returns before your then() code is executed, thus parameterResult is undefined. If you don't want to change your code too much, I would return the Promise you create, like this:
function getParameterFromStore(param){
return new Promise(function(resolve, reject){
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, function(err, data){
if(err){
reject(console.log('Error getting parameter: ' + err, err.stack));
} else {
resolve(data);
}
});
});
};
And finally, on your function's client, you can get the result like this:
const myFirstParam = { Path : '/myPath/Service/servicesEndpoint'}
getParameterFromStore(myFirstParam).then(console.log)
When coding in NodeJS, however, I highly recommend you use async/await instead, so you'll be able to escape the Promise Hell (chaninig Promise after Promise in order to achieve something "synchronously")
When using async/await, you can design your code as though it was synchronous. Here's a refactored version of your example, using async/await as well as arrow functions:
const getParameterFromStore = param => {
return new Promise((resolve, reject) => {
console.log('++ ' + param.Path);
servmgr.getParametersByPath(param, (err, data) => {
if (err) {
console.log('Error getting parameter: ' + err, err.stack)
return reject(err);
}
return resolve(data);
});
})
}
exports.handler = async (event) => {
const endpointResult = await getParameterFromStore(event.someAttributeFromTheEventThatYouWantToUse)
console.log(endpointResult)
};
EDIT: After the OP fixed the first issue, I created a working example on my own. It turned out that the way the OP was invoking the API was incorrect.
Here's the full working example:
'use strict';
const AWS = require('aws-sdk')
AWS.config.update({
region: 'us-east-1'
})
const parameterStore = new AWS.SSM()
const getParam = param => {
return new Promise((res, rej) => {
parameterStore.getParameter({
Name: param
}, (err, data) => {
if (err) {
return rej(err)
}
return res(data)
})
})
}
module.exports.get = async (event, context) => {
const param = await getParam('MyTestParameter')
console.log(param);
return {
statusCode: 200,
body: JSON.stringify(param)
};
};
Mind the Name attribute which must be provided as part of the API call to the ServiceManager.getAttribute method.
This attribute is stated in the official docs
I have run this myself and here's the output in CloudWatch Logs:
As you can see, the value was returned successfully.
Hope this helps!
If your lambda is deployed on VPC, make sure that Security Group is attached to it and outbound traffic is allowed. It will be able to access parameter store automatically.
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-vpc-parameter-store/
A simpler solution would be:
const getParameterFromStore = (params) => servmgr.getParametersByPath(params).promise();
const myFirstParam = { Path : '/myPath/Service'};
getParameterFromStore(myFirstParam).then(console.log);
As you can see, the SDK itself provides utility functinality that you can use depending on your needs to use in an async or syncronious fashion.
Hope it helps.