fileUpload.js
var Uploader = require('s3-image-uploader');
var express = require('express');
var config = require('./../config.js');
var Promise = require('promise');
var uploader = new Uploader({
aws: {
key: config.awsKey,
secret: config.awsSecret
},
websockets: false
});
exports.fileUpload = function(fileName) {
new Promise(function(resolve, reject) {
uploader.upload({
fileId: 'someUniqueIdentifier',
bucket: 'quflip',
source: './public/images/' + fileName,
name: fileName
},
function(data) { // success
console.log('upload success:', resolve);
resolve(data);
},
function(errMsg, errObject) { //error
reject(false);
});
});
};
logintest.js
var awsUpload = require('./../config/fileUpload.js');
var userData = function(req, res) {
console.log("true/false" + awsUpload.fileUpload(profile_image));
Here what is happening is, I am getting undefined value in console.log. So it means that callback is not passing the data. How can I pass the data effectively for further purpose
You need to chain to the promise with .then and do the log there. First, make sure you return the promise
return new Promise(function(resolve, reject) {
Then, you can do:
var userData = function (req, res) {
awsUpload.fileUpload(profile_image).then(data => {
console.log("true/false" + data);
});
}
If you are working in an environment that supports async/await, you can do this a bit more simply:
var userData = async function (req, res) {
console.log("true/false" + await awsUpload.fileUpload(profile_image));
};
Keep in mind that this will return a Promise.
I don't see where profile_image gets set, though.
If you want your function to actually return the promise then change this:
exports.fileUpload = function (fileName) {
new Promise(function(resolve, reject) {
// ...
});
};
to this:
exports.fileUpload = function (fileName) {
return new Promise(function(resolve, reject) {
// ...
});
};
Without the return statement the promise that you're creating is not going to get returned by your function.
But even then you won't be able to use it like this:
console.log("true/false" + awsUpload.fileUpload(profile_image));
You will have to use something like this:
awsUpload.fileUpload(profile_image).then(result => {
console.log("true/false" + result);
});
Or this, if you're using async/await and you're inside async function:
console.log("true/false" + await awsUpload.fileUpload(profile_image));
Related
I'm trying to list all of my cognito users in my lambda function, however i get nothing in the return as if the callback not getting executed. What am I doing wrong?
The output of the code below just gives me a hello in the console.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
export async function main() {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
return err;
} else {
console.log(data);
return data;
}
});
}
First of all, the structure of the code is wrong. The header of Lambda function should have a certain structure, either using async function or non-async function. Since you are using non-async code in your example I will show you how to do the later.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
exports.handler = function(event, context, callback) {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err) // here is the error return
} else {
console.log(data);
callback(null, data) // here is the success return
}
});
}
In this case, Lambda will finish only when callback is called (or when it times out).
Similarly, you can use async function but you will need to restructure your code accordingly. Here is an example taken from official docs. Note how the promise wrapper is used.
const https = require('https')
let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html"
exports.handler = async function(event) {
const promise = new Promise(function(resolve, reject) {
https.get(url, (res) => {
resolve(res.statusCode)
}).on('error', (e) => {
reject(Error(e))
})
})
return promise
}
For AttributesToGet, don't use username because it is one of the fields that always gets returned. The following are members of the Attributes array, and can be used in the AttributesToGet field:
sub, email_verified, phone_number_verified, phone_number, email.
e.g.
AttributesToGet: ["email","email_verified"]
My async function enters then before request is completed. Shouldn't Then part of the code executes only after the async function is completed? How to make the function call only when all the function has finished executing?
app.js
var request_test = require('./request_test');
baseUrl = "https://github.com";
promiseFunction().then((result)=>{
console.log("complete")
});
var promiseFunction = async function promiseFunction() {
request_test.request_test(baseUrl);
}
request_test.js
var request = require('request');
var cheerio = require('cheerio');
var request_test = function check(baseUrl) {
console.log("baseUrl:" + baseUrl)
var options = {
url: baseUrl
};
request(options, function (error, response, html) {
if (!error) {
console.log("no error");
}else{
console.log("else js");
console.log(error);
}
});
}
module.exports = {
request_test: request_test
};
In order to use then() you need to return a promise. So here is an example of the good old style promise chain, simply return promise from request_test and once you resolve or reject it, then() will be called:
promiseFunction().then((result) => {
console.log("complete");
});
function promiseFunction() {
return request_test();
}
function request_test() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("no error");
resolve();
}, 1000);
});
}
Or maybe use the modern approach - async method to await call function that returns promise.
promiseFunction();
async function promiseFunction() {
await request_test();
console.log('complete');
}
function request_test() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log("no error");
resolve();
}, 1000);
});
}
Your issue is coming from var request_test = function check(baseUrl) { ... inside this function you are not returning promise, you are even returning nothing :)
if you are using async I would go ahead and use the await/async syntax. Also the package request does not return a promise, you have an alternative with request-promise-native. The promise should be the return value of your helper function. It could look like this:
var request_test = require('./request_test');
var baseUrl = "https://github.com";
var promiseFunction = async function () {
var result = await request_test.request_test(baseUrl);
console.log("complete");
}
promiseFunction();
and the module:
var request = require('request-promise-native');
var cheerio = require('cheerio');
var request_test = function check(baseUrl) {
console.log("baseUrl:" + baseUrl)
var options = {
url: baseUrl
};
return request(options).then(function (error, response, html) {
if (!error) {
console.log("no error");
} else{
console.log("else js");
console.log(error);
}
});
}
module.exports = {
request_test: request_test
};
I have an asynchronous nightmare.js process which uses vo.js flow control with a generator:
vo(function *(url) {
return yield request.get(url);
})('http://lapwinglabs.com', function(err, res) {
// ...
})
This needs to return a promise to Hapi (v.13.0.0) with reply() interface. I have seen examples with Bluebird and other promise libraries, eg: How to reply from outside of the hapi.js route handler, but having trouble adapting vo.js. Could someone please provide an example of this?
server.js
server.route({
method: 'GET',
path:'/overview',
handler: function (request, reply) {
let crawl = scrape.doCrawl({"user": USERNAME, "pass": PASSWORD});
reply( ... ).code( 200 );
}
});
scrape.js
module.exports = {
DoCrawl: function(credentials) {
var Nightmare = require('nightmare');
var vo = require('vo');
vo(function *(credentials) {
var nightmare = Nightmare();
var result = yield nightmare
.goto("www.example.com/login")
...
yield nightmare.end();
return result
})(credentials, function(err, res) {
if (err) return console.log(err);
return res
})
}
};
If you wanted to send the result of doCrawl to hapi's reply method, you'll have to convert doCrawl to return a promise. Something like this (untested):
server.js
server.route({
method: 'GET',
path:'/overview',
handler: function (request, reply) {
let crawl = scrape.doCrawl({"user": USERNAME, "pass": PASSWORD});
// crawl is a promise
reply(crawl).code( 200 );
}
});
scrape.js
module.exports = {
doCrawl: function(credentials) {
var Nightmare = require('nightmare');
var vo = require('vo');
return new Promise(function(resolve, reject) {
vo(function *(credentials) {
var nightmare = Nightmare();
var result = yield nightmare
.goto("www.example.com/login")
...
yield nightmare.end();
return result
})(credentials, function(err, res) {
// reject the promise if there is an error
if (err) return reject(err);
// resolve the promise if successful
resolve(res);
})
})
}
};
Here is a scenario, I've implemented a loopback remote method which imports some data from REST connector to local postgresql connector.
I can do this for a single model
var importData = function (model, cb) {
migrateModel(model, cb)
.then(findImportInfo)
.then(fetchRemoteData)
.then(processFetchedData)
.then(updateImportInfo)
.then(countLocalData)
.then(importCompleted)
.catch(function (err) {
importFailed(err, cb);
})
.done(function () {
console.log('done');
});
};
So the chain does many thing and at the end importCompleted calls the provide cb which is the callback that returns the response to the REST API.
But I can't figure how to do this with multiple models and return each result. I tried something like this, it works actually but REST API never receives a result.
var importDataAll = function (app, cb) {
var models = app.models();
var deferred = Q.defer();
var promises = [];
var results = [];
function doCallback() {
cb(null, results);
}
models.forEach(function (model) {
if (typeof model.importData === 'function') {
migrateModel(model, model.definition.name, null)
.then(findImportInfo)
.then(fetchRemoteData)
.then(processFetchedData)
.then(updateImportInfo)
.then(countLocalData)
.then(function (prevResult) {
var deferred = Q.defer();
var remoteCount = prevResult.dataCount;
var localCount = prevResult.recordCount;
var result =
{
'time': new Date(),
'remoteCount': remoteCount,
'localCount': localCount,
'started': prevResult.started,
'completed': new Date()
}
results.push(result);
deferred.resolve(result);
return deferred.promise;
})
.catch(function (err) {
promises.reject(err);
})
}
});
return Q.allSettled(promises).then(doCallback);
};
I'm lost at that point, any ideas?
EDIT
Trying #Otze's answer I tried this also
var importDataAll = function (app, cb) {
var models = app.models().filter(function (element, index, array) {
return typeof element.importData === 'function';
});
var promises = models.map(function (model) {
migrateModel(model, model.definition.name, null)
.then(findImportInfo)
.then(fetchRemoteData)
.then(processFetchedData)
.then(updateImportInfo)
.then(countLocalData)
.then(importResult)
.catch(function (err) {
promises.reject(err);
})
});
Q.all(promises)
.then(function (resolvedPromises) {
cb(null, results);
});
};
But the result is the same, cb gets called early but the code actually runs in order. I just can't get the result to the response. I think it's never ends so the REST API gets no content after some time.
Have a look at Q.all or any of the other promise combination functions:
http://documentup.com/kriskowal/q/#combination
With Q.all you could do somehting like this:
var promises = myModels.map(doAllThePromiseThings);
Q.all(promises)
.then(function(resolvedPromises) {
doStuff();
});
Note that you need to return a promise from doAllThePromiseThings.
Since .then returns a promise you can simply do:
.then(function (prevResult) {
return {
'time': new Date(),
'remoteCount': prevResult.dataCount,
'localCount': prevResult.recordCount,
'started': prevResult.started,
'completed': new Date()
};
})
instead of
.then(function (prevResult) {
var deferred = Q.defer();
var remoteCount = prevResult.dataCount;
var localCount = prevResult.recordCount;
var result =
{
'time': new Date(),
'remoteCount': remoteCount,
'localCount': localCount,
'started': prevResult.started,
'completed': new Date()
}
results.push(result);
deferred.resolve(result);
return deferred.promise;
})
I use bluebird library's map method to accomplish such use cases:
https://github.com/petkaantonov/bluebird/blob/master/API.md#mapfunction-mapper--object-options---promise
var Promise = require('bluebird');
var importDataAll = function (app, cb) {
var models = app.models().filter(function (element, index, array) {
return typeof element.importData === 'function';
});
Promise.map(
models,
function(model) {
return migrateModel(model, model.definition.name, null) // don't want to break the promise chain
.then(findImportInfo)
.then(fetchRemoteData)
.then(processFetchedData)
.then(updateImportInfo)
.then(countLocalData)
.then(importResult)
.then(function(){
...
return Promise.resolve(); // don't want to break the promise chain
});
},
{concurrency: 1}
)
.then(function () {
debug('finished working on all the models one-by-one');
cb(null);
})
.catch(function (err) {
cb(err);
});
I've got an express based app running on node.js 0.12.2 which uses the s3.headBucket method from aws-sdk 2.1.22 to return a JSON response depending upon whether a particular bucket exists or not.
I've been struggling to directly stub out the call to s3.headBucket with sinon. I've managed to work around this by creating an s3wrapper module which just requires the aws-sdk and instantiates and returns the s3 variable, however, I'm sure this can be done without using the wrapper module and can instead be stubbed directly with sinon, can anyone point me in the right direction?
Below is the currently working code (with the wrapper module s3wrapper.js which I'd like to remove and handle the stubbing in my status_router_spec.js file). In other words, I'd like to be able to call s3.headBucket({Bucket: 'whatever' ... instead of s3wrapper.headBucket({Bucket: ' ... and be able to stub out this s3.headBucket call with my own response.
status_router_spec.js
var chai = require('chai'),
sinon = require('sinon'),
request = require('request'),
myHelper = require('../request_helper')
var expect = chai.expect
var s3wrapper = require('../../helpers/s3wrapper')
describe('My router', function () {
describe('checking the service status', function () {
var headBucketStub
beforeEach(function () {
headBucketStub = sinon.stub(s3wrapper, 'headBucket')
})
afterEach(function () {
s3wrapper.headBucket.restore()
})
describe('when no errors are returned', function () {
it('returns healthy response', function (done) {
// pass null to represent no errors
headBucketStub.yields(null)
request.get(myHelper.appUrl('/status'), function (err, resp, body) {
if (err) { done(err) }
expect(JSON.parse(body)).to.deep.eql({
healthy: true,
message: 'success'
})
done()
})
})
})
})
})
s3wrapper.js
var AWS = require('aws-sdk')
var s3 = new AWS.S3()
module.exports = s3
status_router.js
var Router = require('express').Router
var s3wrapper = require('../helpers/s3wrapper.js')
var router = new Router()
function statusHandler (req, res) {
s3wrapper.headBucket({Bucket: 'some-bucket-id'}, function (err) {
if (err) {
return res.json({ healthy: false, message: err })
} else {
return res.json({ healthy: true, message: 'success' })
}
})
}
router.get(/^\/status\/?$/, statusHandler)
module.exports = router
Answering this question for the benefit of #ippomakunochi who requested a follow up response.
We ended up using rewire to directly set a stub on the s3 library. For example, we stubbed the getObject call for the s3 library using the following:
s3stub = { getObject: sinon.stub(), listObjects: sinon.stub() }
revert = s3.__set__('s3', s3stub)
Here's the complete code:
../../../build/app/helpers/s3
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
module.exports = {
get: function get(options, callback) {
var requestOptions = { Bucket: module.exports.bucket(), Key: options.productId + '.json' };
s3.getObject(requestOptions, function (err, data) {
if (err) { // handle err }
try {
var productData = JSON.parse(data.Body);
} catch (e) {
// handle error
}
return callback(null, productData);
});
}
}
}
test/unit/app/helpers/s3_spec.js
var AWS = require('aws-sdk')
var chai = require('chai')
var sinon = require('sinon')
var sinonChai = require('sinon-chai')
var chaiSubset = require('chai-subset')
var rewire = require('rewire')
var s3 = rewire('../../../build/app/helpers/s3')
chai.use(chaiSubset)
chai.use(sinonChai)
var expect = chai.expect
describe('S3', function () {
var s3stub, revert
beforeEach(function () {
s3stub = { getObject: sinon.stub(), listObjects: sinon.stub() }
revert = s3.__set__('s3', s3stub)
})
afterEach(function () {
revert()
})
describe('#get', function () {
context('when no errors are returned by s3', function () {
it('returns a product', function (done) {
var productResponse = helper.fixture.body('product.json')
s3stub.getObject.yields(null, productResponse)
s3.get({ productId: '1234' }, function (err, res) {
expect(err).to.not.exist
expect(res).to.containSubset({name: 'long sleeve shirt', 'retailer_code': 'retailer-1'})
done()
})
})
})
context('when s3 returns a NoSuchKey error', function () {
it('returns a NotFoundError', function (done) {
var s3Error = AWS.util.error(new Error(), { name: 'NoSuchKey' })
s3stub.getObject.yields(s3Error)
s3.get({ productId: '1234' }, function (err) {
expect(err.message).to.eql('1234 is not found in s3')
expect(err.output.statusCode).to.eql(404)
done()
})
})
})
})