ES6 node async/await unexpected identifier - javascript

I have the following code that worked when running against babel. Now that I'm using harmony I get the following error:
let adResult = await ad.isUserValid(domainPath, password);
^^
SyntaxError: Unexpected identifier
The following class function:
class ActiveDirectoryHelper {
constructor(options) {
this.config = options.config
this.ad = null;
}
connect() {
var config = {
url: this.config.url,
baseDN: this.config.baseDN,
attributes: this.config.attributes
};
if (this.config.account.user.length > 0) {
config.username = this.config.account.user;
config.password = this.config.account.password;
}
this.ad = new ActiveDirectory(config);
}
async isUserValid(user, password) {
return new Promise((resolve, reject) => {
this.ad.authenticate(user, password, (err, auth) => {
if (err) {
reject({
code: 500,
message: "Unknown authentication error",
entry: {}
});
}
if (auth) {
resolve({
code: 200,
message: "OK",
entry: {
user: user,
password: password
}
});
} else {
reject({
code: 400,
message: "Authentication failed",
entry: {}
});
}
});
});
}
...
exports.ActiveDirectoryHelper = ActiveDirectoryHelper;
I use the class as follows:
const ad = new ActiveDirectoryHelper({
config: adConfig
});
ad.connect();
const domainPath = domain.length > 0 ? `${domain}\\${user}` : user;
const adResult = await ad.isUserValid(domainPath, password);
I run the code using the following parameters:
node --harmony --use_strict --harmony-async-await user.js <my parameters>
If I take the await when calling the method:
const adResult = ad.isUserValid(domainPath, password);
then I don't have the error but it also doesnt wait till the method finishes.
I've googled the error and it seems like your only able to use await within a function that async is in. But without await outside of the method call, it doesnt wait till its finished. Any ideas?

It is because you can't use await unless it's in an async function.
See this link for details:
'await Unexpected identifier' on Node.js 7.5

Related

"ETIMEDOUT connection failed" on nodeJs server after creating new token

The error appeared after generating a new token with an additional scope. May occur with any request to googleapis.com.
I repeatedly generated new tokens with different scopes, but this did not affect anything.
I tried to make requests via POSTMAN when an error occurred, but the requests always succeeded
What could be causing this behaviour?
My connection method:
connect(config) {
const keepaliveAgent = new Agent.HttpsAgent();
instance.defaults = {
maxRedirects: 120,
agent: keepaliveAgent,
retryConfig: {
retry: 5,
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
statusCodesToRetry: [[500, 509, 503, 504, 529, 598, 599]],
shouldRetry: (err) => {
console.log('err', err);
if (err.message.includes('ETIMEDOUT') || err.message.includes('Socket timeout')) return true;
else return false;
},
},
};
return new Promise(async (resolve, reject) => {
if (!config || Object.keys(config).length === 0) {
return reject(new Error('Google Docs settings not found'));
}
this.auth = new google.auth.OAuth2(config.client, config.secret, config.redirect);
const exists = await fw.exists(TOKEN_PATH);
if (exists) {
const token = await fw.read(TOKEN_PATH);
this.auth.setCredentials(JSON.parse(token));
} else {
const token = await this.__generateToken(this.auth);
this.auth.setCredentials(token);
}
this.drive = google.drive({
version: 'v3',
auth: this.auth,
});
this.data = google.docs({
version: 'v1',
auth: this.auth,
});
this.script = google.script({
version: 'v1',
auth: this.auth,
});
resolve();
});
}
I should also note that I am using retryConfig, but this is not the main solution I am looking for.
I tried using the agentkeepalive library. expecting this to reduce the chance of an error occurring. Experimented with the config but all I got was a slightly faster response.

How to assert an error being thrown by mocked method using Jest in AWS Lambda

I have an AWS Lambda function called getTables that runs when you hit an API Gateway endpoint.
I would like to mock an exception being thrown by a section of my code that uses de AWS Glue SDK.
How can I properly mock an exception being thrown by a method and how should I assert it?
The test that I have doesn't seem to be working cause I get an unexpected exception which indicates that another part of the code is trying to use the response of the mocked code (meaning it didn't throw?):
it('should throw on db not found', async () => {
mockGetTables.mockReturnValue(new Error("EntityNotFoundException"));
await getTables(MOCK_REQUEST_EVENT_FAIL, mockContext, mockCallback);
expect(mockCallback).rejects.toEqual('EntityNotFoundException');
});
Here's my Lambda code:
export const getTables = async (
event: APIGatewayProxyEvent,
_context: Context,
callback: Callback<APIGatewayProxyResult>
) => {
console.log('Executing /getTables Activity.');
console.log(`/getTables event: ${JSON.stringify(event)}.`);
try {
const { queryStringParameters } = event;
let catalogIdParam: string = null;
let databaseNameParam: string = null;
let maxResultsParam: number = null;
let nextTokenParam: string = null;
if (queryStringParameters) {
const { catalogId, databaseName, maxResults, nextToken } = queryStringParameters;
catalogIdParam = catalogId || null;
databaseNameParam = databaseName || null;
maxResultsParam = maxResults ? parseInt(maxResults) : null;
nextTokenParam = nextToken || null;
}
const glueClientInstance: GlueClient = GlueClient.getInstance();
//I'd like to mock the following async method and throw "EntityNotFoundException"
//just like API Gateway (or lambda) would do.
const { TableList, NextToken }: GetTablesResponse = await glueClientInstance.getTables(
databaseNameParam,
catalogIdParam,
maxResultsParam,
nextTokenParam
);
const pandaUITableList: PandaUIGlueTable[] = convertToPandaUITableList(TableList);
callback(null, {
statusCode: 200,
body: JSON.stringify({
TableList: pandaUITableList,
NextToken,
}),
});
} catch (error) {
console.log(`An error ocurred while executing /getTables activity: ${JSON.stringify(error)}`);
if (error.code === 'EntityNotFoundException') {
callback(null, {
statusCode: 400,
body: JSON.stringify({
error: error.message,
}),
});
}
//Generic/CatchAll handler that I'll test later once I figure this one out
handlerApiError(error, callback);
}
};
For reference, this is the atual error I'm trying to mock and throw:
{
"message": "Database fdv_lin not found.",
"code": "EntityNotFoundException",
"time": "2022-03-29T00:47:07.475Z",
"requestId": "fff5d84c-59de-441d-a204-e08ede830931",
"statusCode": 400,
"retryable": false,
"retryDelay": 76.52610613917457
}
You can use jest's toHaveBeenCalledWith() or toHaveBeenLastCalledWith() to assert that the callback is called with the appropriate payload in the catch clause. In this case, it is the below example.
callback(null, {
statusCode: 400,
body: JSON.stringify({
error: error.message,
}),
});
Possible Solution
const mockCallback:Callback<APIGatewayProxyResult> = jest.fn();
it('should throw on db not found', async () => {
mockGetTables.mockReturnValue(new Error('EntityNotFoundException'));
await getTables(MOCK_REQUEST_EVENT_FAIL, mockContext, mockCallback);
expect(mockCallback).toHaveBeenCalledWith(null, {
statusCode: 400,
body: JSON.stringify({
error: {
message: 'Database fdv_lin not found.',
code: 'EntityNotFoundException',
time: '2022-03-29T00:47:07.475Z',
requestId: 'fff5d84c-59de-441d-a204-e08ede830931',
statusCode: 400,
retryable: false,
retryDelay: 76.52610613917457,
},
}),
});

Async Function working in Express but not NestJs

I initially created a little express server to run a report and file write function.
var ssrs = require('mssql-ssrs');
var fs = require('fs');
const express = require('express')
const app = express()
const port = 3001
app.get('/', (req, res) => {
reportCreation();
res.send('File Created');
})
app.get('/api', (req, res) => {
reportCreation();
res.json({'File Created': true});
})
app.listen(port, () => {
console.log(`Report Api listening at http://localhost:${port}`)
})
The function reportCreation() is an async function which gets a report from a SSRS. This works fine
async function reportCreation() {
var serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
var reportPath = '/ApplicationPortalReports/TestReportNew';
var fileType = 'word';
var parameters = { ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 }
var auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) console.log('Data written');
});
} catch (error) {
console.log(error);
}
I have been working a lot with NestJs recently and wanted to use the same function but within a NestJs service.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
try {
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
} catch (error) {
console.log(error);
}
console.log(report);
// excel = xlsx
// word = doc
// pdf = pdf
try {
fs.writeFile('ReportApiTest.doc', report, (err) => {
if (!err) { console.log('Data written');
return 'File Written Succesfully'}
});
} catch (error) {
console.log(error);
return 'File Write Error'
}
}
}
As you can see the files are almost identical, but when I run it through NestJs I get an error which looks like a problem with the line
var report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
not awaiting. Why does this work with Express and not NestJS? Below is the error from NestJs
buffer.js:219
throw new ERR_INVALID_ARG_TYPE(
^
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer,
Array, or Array-like Object. Received type undefined
at Function.from (buffer.js:219:9)
at new Buffer (buffer.js:179:17)
at Object.createType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\ntlm.js:172:19)
at sendType3Message (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:77:23)
at Immediate._onImmediate (C:\Projects\SSRS-report-api\ssrs-report-api\node_modules\httpntlm\httpntlm.js:101:4)
within the mssql-ssrs node package the getReportByURL looks like this
async function getReportByUrl(reportPath, fileType, params, auth) {
try {
var config = {
binary: true, // very important
username: auth.userName,
password: auth.password,
workstation: auth.workstation,
domain: auth.domain,
url: soap.getServerUrl()
+ "?" + (testReportPath(reportPath).replace(/\s/g, '+'))
+ "&rs:Command=Render&rs:Format=" + reportFormat(fileType)
+ formatParamsToUrl(params)
};
} catch (err) { report.errorHandler(err) }
return new Promise((resolve, reject) => {
config.url = encodeURI(config.url);
httpntlm.post(config, function (err, res) {
if (res.statusCode === 500) { reject(res) }
if (err || res.statusCode !== 200) { reject(err) }
else { resolve(res.body) }
})
})
}
Here is the app.controller.ts
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): Promise<string> {
return this.appService.getReport();
}
}
This is not an answer for the question. But after I see your code, I can see an error you will face in future if await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth) failed. Actually you see above error because of this.
The way you used the try catch is really bad.
Here's the way I code it.
#Injectable()
export class AppService {
async getReport(): Promise<string> {
const serverUrl = 'http://reportServerName/ReportServer/ReportExecution2005.asmx';
ssrs.setServerUrl(serverUrl);
const reportPath = '/ApplicationPortalReports/TestReportNew';
const fileType = 'word';
// var parameters = {appId: 3, ReportInstanceId: 1 }
const parameters = {ApplicationId: 3, TrainingCardId: 267, PortalPersonId: 52 };
const auth = {
username: 'USERNAME',
password: 'PASSWORD',
domain: 'dmz'
};
const report = await ssrs.reportExecution.getReportByUrl(reportPath, fileType, parameters, auth)
return new Promise(function(resolve, reject) {
fs.writeFile('ReportApiTest.doc', report, , function(err) {
if (err) reject(err);
resolve("File Created");
});
});
}
And in my controller
#POST
async writeFile() {
try {
const res = await this.appService.getReport();
return res;
} catch(err) {
// handle your error
}
}
I had fudged the code in the node_module changing the userName variable to username and had not done the same in the NestJS version. I forgot I had done that so now it is working.

How to return json from callback function within the Lambda?

I'm trying to return the login status from the Cognito callback function, which is written in the NodeJS Lambda. However when I call the API the response keep loading and I'm getting warning error.
Here is my code:
'use strict';
global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
module.exports.hello = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello there"
}),
};
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
module.exports.register = async (event, context, callback) => {
let poolData = {
UserPoolId : 'xxxxx', // Your user pool id here
ClientId : 'xxxxxxx' // Your client id here
} // the user Pool Data
let userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let attributeList = [];
let dataEmail = {
Name : 'email',
Value : 'test#gmail.com'
};
let dataName = {
Name : 'name',
Value : 'Jack'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+94234324324234' // your phone number here with +country code and no delimiters in front
};
let attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
let attributeName = new AmazonCognitoIdentity.CognitoUserAttribute(dataName);
var attributePhoneNumber = new AmazonCognitoIdentity.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributeName);
attributeList.push(attributePhoneNumber);
userPool.signUp('test#gmail.com', 'H1%23$4jsk', attributeList, null, function(err, result){
let data = {};
if (err) {
callback(null, {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
} else {
let cognitoUser = result.user;
callback(null, {
statusCode: 200,
body: JSON.stringify({
status: 'SUCCESS',
message: '',
data: {
username: cognitoUser.getUsername(),
id: result.userSub
}
}),
});
}
})
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
The warning error as follows:
Serverless: Warning: handler 'register' returned a promise and also uses a callback!
This is problematic and might cause issues in your lambda.
Serverless: Warning: context.done called twice within handler 'register'!
serverless.yml
service: test-auth
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
functions:
hello:
handler: handler.hello
events:
- http:
path: message
method: get
register:
handler: handler.register
events:
- http:
path: register
method: post
Any help would be appreciated, Thanks in advance.
EDIT (2019-04-01):
module.exports.register = (event, context) => {
...
userPool.signUp('test#gmail.com', 'H1%23$4jsk', attributeList, null, function(err, result){
// for testing purpose directly returning
return {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
})
}
})
};
Its exactly what the error message states.
All async functions return promises.
module.exports.register = async (event, context, callback) => {}
You are also using the callback by calling
callback(null, {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
Instead of using the callback, just return the either an error or a valid response.
Well the error is accurate. async wraps your return with promise. Either use callback all the way through like:
global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
// remove async
module.exports.register = (event, context, callback) => {
...
// if you're using callback, don't use return (setup your callback to be able to handle this value as required) instead do:
// calback({ message: 'Go Serverless v1.0! Your function executed successfully!', event })
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Or don't use callback, use async/await (Promise) all the way through like:
module.exports.register = async (event, context) => {
...
// needs promise wrapper, when using with promise, you might want to break up your code to be more modular
const mySignUp = (email, password, attributes, someparam) => {
return new Promise((resolve, reject) => {
userPool.signUp(email, password, attributes, someparam, function(err, result) {
let data = {};
if (err) {
reject({
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
} else {
let cognitoUser = result.user;
resolve({
statusCode: 200,
body: JSON.stringify({
status: 'SUCCESS',
message: '',
data: {
username: cognitoUser.getUsername(),
id: result.userSub
}
}),
});
}
})
});
}
// call the wrapper and return
return await mySignUp('test#gmail.com', 'H1%23$4jsk', attributeList, null);
// don't use double return
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Now register will return a promise. Elsewhere in your code you can call register like:
var result = register();
result
.then(data => console.log(data))
// catches the reject from Promise
.catch(err => console.error(err))
or in async/await function (Note: `await` is valid only inside `async` function)
async function someFunc() {
try {
var result = await register();
// do something with result
console.log(result);
} catch (err) {
// reject from Promise
console.error(err)
}
}
Also note use strict is not required here as node modules use strict by default.
You are using an async function with a call back.
Try it this way:
Remove the callback from the async function.
async (event, context)
And modify the return as:
if (err) {
return {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
})
}
}
And put an await on the function call.
If it helps anyone else catching this, you can add headers to the return:
return {
statusCode: 200,
headers: {"Content-Type": "application/json"},
body: JSON.stringify(response.data)
};

Writing jest test for chained functions in node.js

I have a function i want to test with jest, the function basicly does some token verifying and takes 3 params
this is de code of the function i want to test:
const verifyToken = (req, res, next) => {
// check header or url parameters or post parameters for token
var token = req.headers['x-access-token']
if (!token) return res.status(403).send({ auth: false, message: 'No token provided.' })
// verifies secret and checks expire date
jwt.verify(token, config.secret, (err, decoded) => {
if (err) return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' })
//put user inside req.user to use the user in other routes
User.findById(decoded.id, (err, user) => {
if (err) {
return res.status(500).json({
message: err
})
} else if (!user) {
return res.status(404).json({
message: 'No user found'
})
} else {
req.user = user
}
next()
})
})
}
so i'm writing a first test, which tests if no token is given in de request, that it sends a 403 with a message. following is the test.
const verifyToken = require('../../config/token')
describe('veryfiy token tests', () => {
it('Should give 403 status when no token is present', () => {
let mockReq = {
headers: {}
}
var mockRes = {
status: code => code
send: message => message
}
let nextCalled = false
let next = () => {
nextCalled = true
}
expect(verifyToken(mockReq, mockRes, next)).toBe(403)
})
})
Now the test passes with an error:
TypeError: res.status(...).send is not a function
when i removed .send() from res.status in the code, the test passes.
I have been trying to figure out how to mock both status() and send() on the res object. but have not found a solution yet.
Tnx
I think the problem is that the result of res.status() does not have a function called send().
Try using this:
var mockRes = {
status: code => ({
send: message => ({code, message})
}),
};
You should be able to test with:
var result = verifyToken(mockReq, mockRes, next);
expect(result.code).toBeDefined();
expect(result.code).toBe(403);
PS: Haven't tested the code :)
you can make chained mock class and test, wether functions are executed or not.
here is an example.
class MockResponse {
constructor() {
this.res = {};
}
status = jest
.fn()
.mockReturnThis()
.mockImplementationOnce((code) => {
this.res.code = code;
return this;
});
send = jest
.fn()
.mockReturnThis()
.mockImplementationOnce((message) => {
this.res.message = message;
return this;
});
}
and now use this mock class to test. and check given function has executed with given result or not.
example like
it("should not call next function, and return 401, if token has not been found", async () => {
let res = new MockResponse(); // here i initialised instance of class
let next = jest.fn();
let req = {cookies:""} // header or cookies where you are receiving token here in my case empty.
await authentication(req, res, next); // here i used my mock res class
expect(next).not.toHaveBeenCalled(); // you must check that next will not be called.
expect(res.status).toHaveBeenCalledWith(401);//you can check result of status
expect(res.send).toHaveBeenCalledWith("not authenticated");// send() message in your function
});

Categories

Resources