I'm using mocha and supertest to test a REST service. In this case, I need to test that a subsequent login after a GET request returns the query from that GET request.
login user
perform GET request on endpoint(s) using params
login user and expect params from GET request as a response.
Since the above test is going to be performed several times for various endpoints, this is what I have come up with:
/*
* getRequest.js
*/
'use strict';
var request = require('supertest')
, verifyUrl = require('./verifyUrl') // verifies basic url formatting
;
module.exports = getRequest;
/**
* Perform GET request using given parameters and call next when finished.
* #param getEnvironment {json}
* #param getEnvironment.url {string}
* #param getEnvironment.endPoint {string}
* #param getEnvironment.authorization {string}
* #param next {function} The callback
*/
function(getEnvironment, getParams, next) {
var url = verifyUrl(getEnvironment.url);
request(url)
.get(getEnvironment.endPoint)
.set("authorization", getEnvironment.authorization)
.set('Accept', 'application/json')
.query(getParams)
.end(next)
}
/*
* loginWithCallback.js
*
* Performs a basic login but depends on the callback to test the results of the login.
*/
'use strict';
var request = require('supertest')
, VerifyUrl = require('./VerifyUrl')
;
module.exports = LoginWithCallback;
/**
* Perform basic login, then call next passing in err and res for testing.
*
* #param setEnvironment {json} Expects url and authorization. Any other parameters will be ignored.
* #param setEnvironment.url {string} The url being tested.
* #param setEnvironment.authorization {string}
* #param next {function} callback function that will perform the actual test.
*/
function LoginWithCallback(setEnvironment, next) {
var url = VerifyUrl(setEnvironment.url);
request(url)
.post('api/users/login')
.set('authorization', setEnvironment.authorization)
.set('Accept', 'application/json')
.end(next);
}
/*
* e2eTest.js
*/
'use strict';
var base64_encode = require('Base64').btoa
, should = require('should')
, jsonValidator = require('is-my-json-valid')
, mergeJSON = require('lodash/merge')
, lodashClone = require('lodash/clone')
, responseSchema = require('./responseSchemas/200.json')
, login = require('./loginWithCallback')
, getRequest = require('./getRequest')
;
var username = "newUser" + Date.now()
, password = "passW0rd"
, testEnvironment = {
"url": "http://localhost:9000/",
"endPoint": "api/users/login",
"authorization": "Basic " + base64_encode(username + ":" + password)
}
;
var expectedResult = {};
describe('End to End testing on' + JSON.stringify(testEnvironment), function () {
describe('new user, ' + username + ', login', function () {
it('should return 200 and an empty body message.', function (done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err);
}
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" +
JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
describe('user, ' + username + ', logs in after performing get request', function () {
var getRequestParams = {"firstName":"john", "lastName":"doe"};
beforeEach(function() {
var getEnviron = lodashClone(testEnvironment);
getEnviron.endPoint = 'api/persons/findName';
mergeJSON(expectedResult, {"persons": {"findName": getRequestParams}});
getRequest(getEnviron, getReqestParams, function(err, res) {
if (err) {
done(err);
}
console.log("GET request: " JSON.stringify(res, null, 2));
done();
});
});
it('should return 200 and query values', function(done) {
login(testEnvironment, function (err, res) {
if (err) {
done(err)
}
console.log("it test: " + JSON.stringify(res, null, 2));
res.statusCode.should.deepEqual(200);
var jsonValidate = jsonValidator(responseSchema, {verbose: true});
jsonValidate(res.body).should.be.true("Response body failed schema check: \n" + JSON.stringify(jsonValidate.errors, null, 4));
res.body.should.deepEqual(expectedResult);
done();
});
});
});
What the above three files are supposed to do is the following:
1. create a new user
2. login new user and test its response for no previous query params. (passes)
3. perform the GET request and then print the result in the before block
4. perform the login of the user print and test results in the it block.
But what I'm getting is something like:
End to End testing on{"url":"http://localhost:9000/","endPoint":"api/users/login","authorization":"Basic bmV3VXNlcjE0NjY0NDI0OTEzNDc6cGFzc1cwcmQ="}
new user, newUser1466442491347, login
✓ should return 200 and an empty body message. (54ms)
user, newUser1466442491347, logs in after performing persons/findByName request
it test: [res text]
1) should return and query values
GET request: [res text]
1 passing
1 failing
Uncaught AssertionError: expected Object {} to equal Object {
persons: Object { findByName: Object { firstName: 'joe', lastName: 'jones' } }
}
As can be seen, the 'it' block is appearing to run before the before block has finished. From my reading of mocha, this should not happen as it waits for the before and beforeEach finishes before running the 'it' block. But, perhaps, the callback for the getRequest gets queued after the it block?
What am I doing wrong or misunderstanding?
You definitely must set your beforeEach hook so that Mocha knows it is asynchronous: either you return a promise to Mocha or you use the done callback. In order to use the done callback you must declare a callback that takes done:
beforeEach(function (done) {
(You could call it anything you want (e.g. finished), so long as you are consistent (call finished() later).)
Related
I'm trying to make a script that makes a shared drive from the google drive api but right now I'm getting an error inside the createSharedDrive(auth) function where it says
drive.drives.create
as it's saying the 'create' function is undefined. Not sure how to fix it. The google drive api documentation doesn't show the entire script to create a shared drive.
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const { uuid } = require('uuidv4');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
//authorize(JSON.parse(content), listFiles);
authorize(JSON.parse(content), createSharedDrive);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* #param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
resource: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}
The create method does not contains any "resource" property. It contains a requestId property and a requestBody property; Therefore, your request should look like this:
function createSharedDrive(auth){
const drive = google.drive({version: 'v3', auth});
var driveMetadata = {
'name': 'Testing Create Shared Drive'
};
var requestId = uuid;
drive.drives.create({
requestBody: driveMetadata,
requestId: requestId,
fields: 'id'
}, function (err, drive) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('Drive Id: ', drive.id);
}
});
}
I wrote the below code to assign session attributes from my application,
var lexruntime = new AWS.LexRuntime();
var params = {
botAlias: 'PizzaOrder', /* required */
botName: 'PizzaOrder', /* required */
inputText: 'profile list', /* required */
userId: '160728846416', /* required */
sessionAttributes: {
//'<token>': cookies['token'],
'token': cookies['token'],
'communityid':cookies['communityid'],
'selectedAuthorId':cookies['selectedAuthorId'],
'kfurl':cookies['kfurl']
/* '<String>': ... */
}
};
//console.log("Cookies in index js "+cookies['token']);
lexruntime.postText(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
I want to access the session attributes in my lambda function using intentRequest.sessionAttributes.seesionattributename in the below code and as shown below
function GetAuthors2(intentRequest,callback) {
const token2 = String(intentRequest.sessionAttributes.token);
const communityid2 = String(intentRequest.sessionAttributes.communityid);
// --------- http start ---------
var options = {
hostname: 'kf6-stage.rit.albany.edu',
port: 443,
path: '/api/communities/'+communityid2+'/authors',
headers: {
'Content-Type' : "application/json",
'Authorization' : "bearer " + token2
}
};
https.get(options, (resp) => {
console.log("#in Http");
let data2 = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data2 += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
var data_2 = JSON.parse(data2);
const cList2 = [];
data_2.forEach(function(i){
cList2.push(i.firstName);
});
callback(close(intentRequest.sessionAttributes, 'Fulfilled',
{
contentType: "PlainText or SSML",
content: "Hi,You have "+cList2.length+" authors registered Bharghav. Here is the list \n"+ cList2.join()
}
));
});
});
}
but after the intent request I am getting the following response "Sorry, I was unable to process your message. Try again later."
If I hardcode the values in the lambda function it works fine.
Can someone kindly tell me where am I doing wrong in accessing the session attributes??
This worked for me. accessing the session attributes from the event/input. Then accessing the attribute by name.
exports.handler = async (event) => {
var attributes = event['sessionAttributes'];
console.log('attrs; ',attributes);
let sessValue;
if( attributes != null)
sessValue = attributes['fileName'];
I have this redux saga code where everything works okay...until the promise, after that things start to go wrong
here's the relevant code
const firstApiRequest = ()=>{
return $.ajax({
url: myUrl,// ,
type:'POST',
headers: {
"Accept":"application/json",
"Content-Type":"application/json",
},
data:JSON.stringify(bodyData),
success:function(res){
console.log(res);
return res;
}
})
};
export function *startCheckout() {
try {
yield put(showLoading());
const data = yield call(firstApiRequest);//const data ends
yield put({type:FIRST_REQUEST_DONE,payload:data});
} catch (err) {
yield put(firstRequestFail(err));
}
}
export function *checkout() {
yield takeEvery(SEND_FIRST_REQUEST, startCheckout);
}
The problem is that after the return res in firstApiRequest , I wanted to use the data to send the FIRST_REQUEST_DONE action , but what happens is that the flow goes to FIRST_REQUEST_FAIL and shows error as true.
The problem is that the api response is coming back successfully and I am getting the data inside the error when the flow goes to FIRST_REQUEST_FAIL part of reducer and data shows up as error.
here's the code for reducer
where flow goes to
case 'FIRST_REQUEST_FAIL':
return {
loading: false,
error: true,
errorMessage: action.err,
};
instead of going to
case 'FIRST_REQUEST_DONE':
return {
id: action.id,
};
so, what's wrong with the code here? why does it show error even after a succesful response from server?
You shouldn't be defining the success in your api request.
$.ajax will return a promise on its own:
const firstApiRequest = () => (
$.ajax({
url: myUrl,// ,
type:'POST',
headers:{
"Accept":"application/json",
"Content-Type":"application/json",
},
data:JSON.stringify(bodyData),
}));
Also, why are you using jQuery for making the API requests? I'd suggest using axios or fetch
Here is an approach to handle API request using redux-saga:
First create a request helper
import 'whatwg-fetch';
function parseJSON(response) {
return response.json ? response.json() : response;
}
/**
* Checks if a network request came back fine, and throws an error if
not
*
* #param {object} response A response from a network request
*
* #return {object|undefined} Returns either the response, or throws an
* error
*/
function checkStatus(response, checkToken = true) {
if (response.status >= 200 && response.status < 300) {
return response;
}
return parseJSON(response).then(responseFormatted => {
const error = new Error(response.statusText);
error.response = response;
error.response.payload = responseFormatted;
throw error;
});
}
/**
* Requests a URL, returning a promise
*
* #param {string} url The URL we want to request
* #param {object} [options] The options we want to pass to "fetch"
*
* #return {object} The response data
*/
export default function request(url, options = {}) {
// Set headers
if (!options.headers) {
options.headers = Object.assign({
'Content-Type': 'application/json',
}, options.headers, {});
}
// Stringify body object
if (options && options.body) {
options.body = JSON.stringify(options.body);
}
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
}
In your saga
import { call, fork, put, takeLatest } from 'redux-saga/effects';
import request from 'utils/request';
import { submitSuccess, submitError } from './actions'; // path
to your actions.
import { SUBMIT } from './constants'; // The event you're listening
export function* submitData(action) {
try {
const response = yield call(request, 'your_url', { method: 'POST', body: action.body });
yield put(submitSuccess(response));
} catch(err) {
yield put(submitError(response.payload.message);
}
}
export function* defaultSaga() {
yield fork(takeLatest, SUBMIT, submitData);
}
export default defaultSaga;
Reducer
const initialState = fromJS({
submitSuccess: false,
submitReponse: '',
errorMessage: '',
});
function fooReducer(state = initialState, action) {
switch (action.type) {
case SUBMIT_SUCCESS:
return state
.update('submitSuccess', () => true)
.update('submitResponse', () => action.response);
case SUBMIT_ERROR:
return state.update('errorMessage', () => action.errorMessage);
//...
}
}
With this structure you should be able to catch your success and you error when you're making your request.
I am using const google = require('googleapis'); to stream into google bigquery but now when I want to select my database I am puzzled.
Looking into the documentation I need to do use bigquery.jobs.query but I don`t understand where the actual select should be placed.
var query = `select 1 `;
bqBooking.auth = jwtClient;
bigquery.jobs.query(bqBooking, function (err, rows) {
if (err) {
return callback(err);
}
printExample(rows);
callback(null, rows);
});
/**
* Run an example query.
*
* #param {Function} callback Callback function.
*/
function queryExample (callback) {
var query = 'SELECT TOP(corpus, 10) as title, COUNT(*) as unique_words\n' +
'FROM [publicdata:samples.shakespeare];';
bigquery.query(query, function (err, rows) {
if (err) {
return callback(err);
}
printExample(rows);
callback(null, rows);
});
}
https://cloud.google.com/bigquery/create-simple-app-api
Can pass query as parameter in 'Request Body' of bigQuery.jobs.query example as shown in link Please use 'Try this API' option in 2nd link.
var google = require('googleapis');
var bigquery = google.bigquery('v2');
authorize(function(authClient) {
var request = {
// Project ID of the project billed for the query
projectId: '', // TODO: Update placeholder value.
resource: {
// TODO: Add desired properties to the request body.
"query": "Select channel, sum(totalRequests) from
conversation_logs.RequestSummary WHERE timeStamp >
TIMESTAMP('2017-09-03 00:00:00 UTC') Group by channel;",
"maxResults": 1,
"useLegacySql": false
},
auth: authClient
};
bigquery.jobs.query(request, function(err, response) {
if (err) {
console.log(err);
return;
}
// TODO: Change code below to process the `response` object:
console.log(JSON.stringify(response, null, 2));
});
});
function authorize(callback) {
google.auth.getApplicationDefault(function(err, authClient)) {
if (err) {
console.log('authentication failed: ', err);
return;
}
if (authClient.createScopedRequired &&
authClient.createScopedRequired()) {
var scopes = ['https://www.googleapis.com/auth/cloud-
platform'];
authClient = authClient.createScoped(scopes);
}
callback(authClient);
});
}
My question was voted down, so i am rewriting it hopefully this is more succinct
I am stuck at writing a javascript function to list messages in inbox.
Using - official "google-api-nodejs-client", node.js, electron, (and javascript)
Goal: list messages in gmail inbox
For that to work i need to authorize first then ask for the messages
Authorize
- I copied the code from google node.js quickstart
- this works in electron (well actually node.js as it is a command line script).
Ask for Messages
- Google has an example, i copied it adjusted some parts but doesn't work. I think the example i am working from is not designed for the node.js "google-api-nodejs-client".
- Maybe it needs a different authorize
This is the listmessages function from the google example, i can't seem to figure out how to make this work with the authorize from list labels. This is what i have tried
changing gapi to google
changing userId to 'me'
changing givig it a query
Does not use a client library.
Does not use a client library.
/**
* Retrieve Messages in user's mailbox matching query.
*
* #param {String} userId User's email address. The special value 'me'
* can be used to indicate the authenticated user.
* #param {String} query String used to filter the Messages listed.
* #param {Function} callback Function to call when the request is complete.
*/
function listMessages(userId, query, callback) {
var getPageOfMessages = function(request, result) {
request.execute(function(resp) {
result = result.concat(resp.messages);
var nextPageToken = resp.nextPageToken;
if (nextPageToken) {
request = gapi.client.gmail.users.messages.list({
'userId': userId,
'pageToken': nextPageToken,
'q': query
});
getPageOfMessages(request, result);
} else {
callback(result);
}
});
};
var initialRequest = gapi.client.gmail.users.messages.list({
'userId': userId,
'q': query
});
getPageOfMessages(initialRequest, []);
}
This is the aurhorize function that works to list labels.
var fs = require('fs');
var readline = require('readline');
var google = require('googleapis');
var googleAuth = require('google-auth-library');
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
var TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH ||
process.env.USERPROFILE) + '/.credentials/';
var TOKEN_PATH = TOKEN_DIR + 'gmail-nodejs-quickstart.json';
// Load client secrets from a local file.
fs.readFile('client_secret.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), listLabels);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
*
* #param {Object} credentials The authorization client credentials.
* #param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
var clientSecret = credentials.installed.client_secret;
var clientId = credentials.installed.client_id;
var redirectUrl = credentials.installed.redirect_uris[0];
var auth = new googleAuth();
var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
oauth2Client.credentials = JSON.parse(token);
callback(oauth2Client);
}
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
*
* #param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for.
* #param {getEventsCallback} callback The callback to call with the authorized
* client.
*/
function getNewToken(oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
console.log('Authorize this app by visiting this url: ', authUrl);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('Enter the code from that page here: ', function(code) {
rl.close();
oauth2Client.getToken(code, function(err, token) {
if (err) {
console.log('Error while trying to retrieve access token', err);
return;
}
oauth2Client.credentials = token;
storeToken(token);
callback(oauth2Client);
});
});
}
/**
* Store token to disk be used in later program executions.
*
* #param {Object} token The token to store to disk.
*/
function storeToken(token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
console.log('Token stored to ' + TOKEN_PATH);
}
/**
* Lists the labels in the user's account.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listLabels(auth) {
var gmail = google.gmail('v1');
gmail.users.labels.list({
auth: auth,
userId: 'me',
}, function(err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
var labels = response.labels;
if (labels.length == 0) {
console.log('No labels found.');
} else {
console.log('Labels:');
for (var i = 0; i < labels.length; i++) {
var label = labels[i];
console.log('- %s', label.name);
}
}
});
}
The flow of your program should look like this:
Then you can use the api to list messages. (just to get you started):
export function listMessages(oauth2Client, userId, query, callback) {
const gmail = google.gmail('v1');
gmail.users.messages.list({
auth: oauth2Client,
userId: 'me',
}, (err, response) => {
console.log(response);
});
}
Notice the oauth2Client parameter. This is the object you get from your function authorize. Let me know if you have any questions.