AWS Lambda: exports.handler not running promise functions - aws-lambda

This is not working:
exports.handler = (username, password) => {
return {
login: () => login(username, password),
processLogin,
generateReport
};
};
When I change to this it works, but...
exports.handler = (username, password) => {
login(username, password);
processLogin();
generateReport();
};
...it doesn't follow the order. How to make it work on AWS Lambda? In my understanding, the process is follows, first login is run, when it is done processLogin starts, when it is done generateReport starts. generateReport looks like this:
function generateReport(token)...
which means that the functions grabs some argument from processLogin which runs before that. Anyways, how do I make it work?
Please let me know if additional info needed.

Your first version does not return a Promise, but instead returns an object. No code is executed. Additionally, the handler parameters should match the AWS API.
Your code should explicitly return a promise:
exports.handler = (event, context) => {
username = extractUsername(event);
password = extractPassword(event);
return Promise.resolve()
.then(() => login(username, password))
.then(() => processLogin())
.then(() => generateReport())
.catch(err) => handleError());
}

Related

Is it possible to return or await for return of cypress intercept?

I would like to get the response of an intercept cypress call while running an e2e test:
const registerUser = async (username, password)=>{
cy.visit('<user_registration_url');
cy.intercept('/registration').as('registration');
cy.get('input#username').type(username);
cy.get('input#password').type(password);
cy.get('button#submit').click()
// Expected response:
//{statusCode: 200, body: {userId: <string>}}
const result = await cy.wait('#registration');
const userId = result.response.body.userId;
return userId
it('should test something with a newly registered user', ()=>{
const userId = registerUser('foobar', 'password');
// ... keep testing using the userId which was generated on the backend.
});
No, this won't work.
Cypress is asynchronous, so the commands won't return any data that you can assign to variables. You can read more on then in Cypress docs.
You can get to the response like so:
cy
.wait('#registration')
.then(response => {
// some other code
});
or you can check e.g. a status code like so:
cy
.wait('#registration')
.its('response.statusCode')
.should('eq', 201);
You can see many examples on the page for cy.intercept command.
So indeed as #pavelsman mentioned there is no async/await syntax for cypress. In order to make utility functions one can return a chainable as in this example:
const registerUser = (username, password)=>{
cy.visit('<user_registration_url');
cy.intercept('/registration').as('registration');
cy.get('input#username').type(username);
cy.get('input#password').type(password);
cy.get('button#submit').click()
// Expected response:
//{statusCode: 200, body: {userId: <string>}}
return cy.wait('#registration');
it('should test something with a newly registered user', ()=>{
registerUser('foobar', 'password').then(result=>{
const userId = result.response.body.userId;
// ... keep testing using the userId which was generated on the backend.
});
});

Node promise request works locally but not on lambda

For some reason, the script that I am running locally does not work when uploaded to aws lambda. What's weird is that I neither get an error message nor a result. The request simply seems to get stuck and times out.
const request = require('request-promise-native');
... async function (username, password) {
if (await this.login(username, password)) {
console.log("Logged in") //works locally
return Promise.resolve();
} else {
console.log("Not logged in") // never gets called
}
Con.prototype.login = async function (username, password) {
console.log("login") //gets printed
return new Promise(resolve => {
try {
const ccc0 = this.getCookies()
console.log("login cookies:", ccc0) //gets printed
request.post({
url: `${loginURL}/login`,
headers: {
'Content-Type': 'application/json',
'Cookie': ccc0
},
json: { 'username': username, 'password': password },
resolveWithFullResponse: true
}).then((loginResult) => {
console.log("loginResult") //local reaches here, lambda does not
resolve(true)
});
console.log("Check 2") //gets printed
} catch (e) {
console.log("Login failed:") //does not get printed
console.log(e);
resolve(false)
}
});
}
At first I thought of conflicting promises or something in that regard however when even the console.logs get beyond that state, what could possibly make the request get stuck like that?
Unless Lambda functions have firewalls or something like that by default, I haven't set up anything.
The issue was setting a VPC having the potential of breaking your internet connection.
https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/

Executing code directly throws exception correctly but within a function doesn't

I'm trying to throw exception in the async code. If I throw it directly like this, it works fine. Example:
Query: {
requisitions: async (parent, args, { req }, info) => {
const user = await Auth.findOne({ _id: req.session.userId }).select('roles')
if (!user.roles.some(role => ['driver', 'requestant'].includes(role))) {
throw new ForbiddenError(ErrorCodes.UNAUTHENTICATED)
}
return true
}
}
But if I make a helper function to handle this, it doesn't work. I need the helper function because this roles check is needed at many places in the app.
Query: {
requisitions: async (parent, args, { req }, info) => {
hasRights(req, ['driver', 'requestant'])
return true
}
}
export const hasRights = async (req, allowedRoles) => {
const user = await Auth.findOne({ _id: req.session.userId }).select('roles')
if (!user.roles.some(role => allowedRoles.includes(role))) {
throw new ForbiddenError(ErrorCodes.UNAUTHENTICATED)
}
}
Note: the hasRights is exported from another file.
I have tried all combinations of try/catch blocks in both files but I just get the UnhandlePromiseRejectionWarning in the server console but the requisitons resolver continues in execution. I want it to stop if the exception is thrown. I have no idea why it only works one way and not the other, even though it is the same thing. Any help appreciated. Thanks.
Calling hasRights(req, ['driver', 'requestant']) can return a rejected promise, but you have no code to ever handle that. As such, it's an unhandled rejection.
When the throw is inline, it's caught by the async requsitions() function which then returns a rejected promise so the caller of requisitions() has a chance to catch it.
You can make sure a rejected promise returned from hasRights() is handled the same way by adding await in front of the hasRights() call like this:
Query: {
requisitions: async (parent, args, { req }, info) => {
await hasRights(req, ['driver', 'requestant'])
return true
}
}
This will allow the async requisitions() function to automatically catch a rejection from hasRights() the same way your original inline throw did.
You will, of course, have to make sure you handle rejected promises properly in the callers of requisitions().
It also occurs to me that you need to know that executing a throw inside an async function will be automatically caught by the async function and converted into a rejection of the promise that is returned by the async function. This is a feature of async functions.

is empty response with status 200 possible via aws serverless?

So I want the response body to be empty. I tried varionations of the following:
export const hello = async (event, context, callback) => {
callback(null, null)
}
However this returns a string 'null'.
If I don't invoke the callback lambda responds with a timeout error or something.
callback is expecting an HTTP response object in second argument. Try like this.
exports.handler = async (event, context, callback) => {
callback(null, {
statusCode: '200',
body: ''
});
};

testing custom redux middleware, which handles ajax request

I am building a react-redux app, using custom redux middleware.
In the definition of my project, action only provides an object to define action type and necessary parameters for middleware and reducer. All the ajax request will be handle by middleware. This is the life cycle would look like:
action -> middleware(if action is intercepted) -> reducer -> store
When the user tries to log in, the operation on the react component will fire an action, which would look like this:
export function login(username, password) {
return {
type: 'LOGIN',
username: username,
password: password
}
}
export function authSucceed(username, isAdmin) {
return {
type: 'AUTHSUCCEED',
username: username,
isAdmin: isAdmin
}
}
export function authFail(text) {
return {
type: 'AUTHFAIL',
errorMessage: text
}
}
Then middleware will use the parameters passed in action to send ajax request, which would be like this.
export function customedMiddleware(store) {
return next => action => {
if (action.type === 'LOGIN') {
axios.post(url + '/api/login', {
username: action.username,
password: action.password
})
.then(res => {
if (res.status === 200) {
store.dispatch(actions.authSucceed(res.data.username, res.data.isAdmin));
} else {
store.dispatch(actions.authFail(res.data));
}
})
.catch(error => console.log(error));
}
return next(action);
};
}
After the middleware sends login request to server, depending on whether the authentication succeeds or not, the middleware will dispatch some action in reducer correspondingly. Since authSucceed and authFail would not be intercepted by middleware, reducer will process accordingly.
export default function(state = false, action) {
switch(action.type) {
case 'AUTHSUCCEED':
return true;
case 'AUTHFAIL':
return false;
case 'LOGOUT':
return false;
}
return state;
}
What has been done here in reducer is to change the system state. If the state is true, the front-end will render the information page. If the state is false, the front-end will remain in the login page.
I like system definition this way. Every MVC part is well isolated. However, it's very difficult to test the middleware. Currently, I am testing this way:
it('should dispatch authSucceed if signup with correct info', () => {
nock('http://localhost:8080')
.post('/api/signup', {
username: 'bruce',
password: 'Gx1234'
})
.reply(200, {
username: 'bruce',
isAdmin: false
});
const createStoreWithMiddleware = applyMiddleware(customedMiddleware)(createStore);
const store = createStoreWithMiddleware(reducers);
const dispatch = sinon.spy(store, 'dispatch');
store.dispatch(actions.login('bruce', 'Gx1234'));
setTimeout(() => {
expect(dispatch.calledWith({
type: 'AUTHSUCCEED',
username: 'bruce',
isAdmin: false
})).to.be.true;
}, 100);
});
I dispatch login action. Then spy the whether authSucceed action and authFail action will be called correctly within 100ms. This method works if there is only one test to be run. If there are more then one test running in sequence, they might affect each other. I have to adjust the time delay of the setTimeout to make it work for all cases, which is 10ms.
I don't feel comfortable this way. I can't make sure whether it just work for me or for everybody too, since absolute time is related to hardware.
I would really appreciate if anybody can give me some advice on how to test this custom middleware.
Your code works fine, but you shouldn't need a setTimeout with such a long time since using nock makes the remote resquest response instantly. The problem is promises enqueue microtasks and they only run after a macrotask is finished (in your case, it()), in the same event loop.
That's why you need setTimeout to enqueue another macrotask, the time doesn't make a difference. I believe setImmediate should work as well.

Resources