I am using Node JS, mocha and googleapi writing tests to verify email content
When i run the googleapi as a standalone node js file i am able to get mails but when i integrate it with mocha tests i am not seeing any result, please help
test spec file (verify.js)
var checkEmail = require('../shared/checkEmail');
const { google } = require('googleapis');
var expect = require('chai').expect;
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
const { isNullOrUndefined } = require('util');
chai.use(chaiAsPromised);
chai.should();
var emailRecords = new Array();
var content;
var auth;
describe('GMAIL Testing', function () {
before('Connect to Gmail server', function () {
content = checkEmail.getAuthentication();
auth = checkEmail.authorize(content);
// Random test data
var a = [{ "Id": "123", "MsgId": "34677", "Type": "aaa", "Subject": "subxxxx", "ToAddress": "abc#gmail.com", "ToName": "ABC", "DateCreated": "2020-07-09T18:25:38.047Z" }];
emailRecords.push(a);
var b = [{ "Id": "456", "MsgId": "34655", "Type": "bbb", "Subject": "subject", "ToAddress": "abc#gmail.com", "ToName": "ABC", "DateCreated": "2020-06-09T18:25:38.047Z" }];
emailRecords.push(b);
});
it('Gmail Verification', function () {
emailRecords.forEach(element => {
const gmail = google.gmail({ version: 'v1', auth });
var query = "from:noreply#somedomain.com " + element[0].MsgId;
console.log('getting mail '+ element[0].MsgId);
gmail.users.messages.list({
userId: 'me',
q: query
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
var mails = res.data.messages;
console.log('mail(s) found');
expect(mails.length).to.be.at.least(1);
});
console.log('completed search');
});
});
});
Utility File checkEmail.js Ref -> Gmail API
const fs = require('fs');
const readline = require('readline');
const { google } = require('googleapis');
var base64 = require('js-base64').Base64;
const cheerio = require('cheerio');
var open = require('open');
const { isNullOrUndefined } = require('util');
var Mailparser = require('mailparser').MailParser;
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
// 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';
module.exports = new class checkEmail {
getAuthentication() {
// Load client secrets from a local file.
console.log('getting auth');
this.content = JSON.parse(fs.readFileSync('shared/config/credentials.json', 'utf8'));
return this.content;
}
authorize(credentials) {
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
var token = fs.readFileSync(TOKEN_PATH, 'utf-8');
if (token == isNullOrUndefined) {
token = getNewToken(oAuth2Client);
}
oAuth2Client.setCredentials(JSON.parse(token));
return oAuth2Client;
}
getNewToken(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
var newToken;
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);
});
newToken = token;
});
});
return newToken;
}
}
I have tried adding debug messages but nothing is printed nor any error is thrown, Please let me know if i am missing something
output after running tests
> .\node_modules.bin\mocha .\test\verify.js
Tests are appearing to be passed but console.log('mail(s) found');
didnt show up in the output
Your test is finishing before your network request finishes.
See this section in the mocha docs.
https://mochajs.org/#asynchronous-code
You need to either use the done callback or return a promise.
If you can use async/await I find this to be the easiest because an async function always returns a promise: https://mochajs.org/#using-async-await
Related
I have a Desktop App for Google Drive that create and set perms that i need. This app should work only for one account with google drive.
My problem is that when I launch an action, I always have to confirm this action in explorer. Can I somehow automatically send my data and confirmation to the server? I read about the access token, but it seems to be suitable only for web applications. I am based on the documentation from the Google API site.
And in future this should work from console.
My code right now:
const fs = require('fs').promises;
const path = require('path');
const process = require('process');
const {authenticate} = require('#google-cloud/local-auth');
const {google} = require('googleapis');
const { AuthClient } = require('google-auth-library');
const SCOPES = ['https://www.googleapis.com/auth/drive'];
const TOKEN_PATH = path.join(process.cwd(), 'token.json');
const CREDENTIALS_PATH = path.join(process.cwd(), 'credentials.json');
async function loadSavedCredentialsIfExist() {
try {
const content = await fs.readFile(TOKEN_PATH);
const credentials = JSON.parse(content);
return google.auth.fromJSON(credentials)
} catch (err) {
return null;
}
}
async function saveCredentails(client) {
const content = await fs.readFile(CREDENTIALS_PATH);
const keys = JSON.parse(content);
const key = keys.installed || keys.web;
const payload = JSON.stringify({
type: 'authorized_user',
access_type: 'offline',
client_id: key.client_id,
client_secred: key.client_secret,
refresh_token: client.credentials.refresh_token,
});
await fs.writeFile(TOKEN_PATH, payload);
}
async function authorize() {
let client = await loadSavedCredentialsIfExist();
if (client) {
return client;
}
client = await authenticate({
scopes: SCOPES,
keyfilePath: CREDENTIALS_PATH,
});
if (client.credentials) {
await saveCredentails(client);
}
return client;
}
async function createFolder(authClient) {
const service = google.drive({version: 'v3', auth: authClient});
const fileMetadata = {
name: 'testmeows',
mimeType: 'application/vnd.google-apps.folder',
};
try {
const file = await service.files.create({
resource: fileMetadata,
fields: 'id',
});
console.log('Folder Id:', file.data.id);
const body = {"role": "writer", "type": "anyone"}
const result = await service.permissions.create({
resource: body,
fileId: file.data.id,
//fields: 'id',
});
const align = `https://drive.google.com/drive/folders/${file.data.id}?usp=sharing`;
console.log(align);
} catch (err) {
throw err;
}
}
//module.exports = test;ф
authorize().then(createFolder).catch(console.error);
Well, how to better get refresh token without user invention and opening explorer on Desktop App Google Api?
I am working on oauth2 for an API client, and I am having trouble getting the promise back in time to continue with the other calls to the API. My tokens expire every 30 minutes and my node runs every 10 minutes. I thought I could set a text file to the newest token each time the script runs, and grab it at the beginning and always have a good token to use for auth. The variable doesn't get set in time to make the calls, so the header has undefined next the Bearer. I can't figure out how to get the variable set before the call goes off.
Here is the script I am using to make the calls
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Axios = require("axios");
const moment = require("moment");
const config = require("../config/default");
const oauth = require("axios-oauth-client");
const fs = require('fs');
let accessToken;
var token = fs.readFile('token.txt', 'utf8', function(err, data) {
if (err) throw err;
return data;
});
const getOwnerCredentials = oauth.client(Axios.create(), {
url: 'url',
grant_type: 'password',
client_id: 'username',
client_secret: 'secret',
username: 'username',
password: 'password',
scope: 'scope'
});
const tokenCall = async () => {
const result = await getOwnerCredentials();
return result
}
const getToken = async () => {
const accessToken = await tokenCall();
const fs = require('fs')
fs.writeFile('/root/qt-cwsedona/token.txt', '', function(){console.log('done')})
fs.writeFile('token.txt', accessToken.access_token, function (err) {
if (err) return console.log(err);
});
};
getToken();
class Sedona {
constructor() {
this.baseUrl = config.sedonaUrl;
this.client = Axios.default.create({
baseURL: this.baseUrl,
auth: {
Authorization: 'Bearer ' + token
},
});
console.log(this.client);
}
getCustomerBillId(customer_id) {
return this.client.get('/CustomerBill/' + customer_id).then(response => {
let result = parseInt(response['data'][0]['CustomerBillId']);
if (isNaN(result)) {
return "";
}
else {
return result.toString();
}
}).catch(error => {
throw error;
});
}
Then I am this code to actually kick off these functions
const Sedona = require("./services/SedonaService");
Promise.all([
sedona.getCustomerBillId(customerId)
]).then(sedona_results => {
This is probably not the right way to do this, but I was able to set the default header for axios in the async function and it worked.
const getToken = async () => {
const accessToken = await tokenCall();
const token = accessToken.access_token;
Axios.defaults.headers.common['Authorization'] = 'Bearer '+accessToken.access_token;
};
I would like use Firebase Functions to use the Google Developer API. Authentification is required to use this API.
I follow the doc: https://github.com/googleapis/google-api-nodejs-client
I have some troubles to get the authorization code in the callback url.
var {google} = require('googleapis');
google.options({ auth: oauth2Client });
var oauth2Client = new google.auth.OAuth2(
'XXXX.apps.googleusercontent.com',
'XXXX',
'https://us-central1-XXXX.cloudfunctions.net/oauth2callback'
);
function generateAuthenticationUrl() {
return oauth2Client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: 'https://www.googleapis.com/auth/androidpublisher'
});
}
exports.oauth2Callback = functions.https.onRequest((req, res) => {
console.log(req.query.code);
const code = req.query.code;
//do something
return null;
});
exports.hello = functions.https.onRequest((req, res) => {
var url = generateAuthenticationUrl();
console.log(url);
//-> url print in the console is : https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fandroidpublisher&response_type=code&client_id=XXXXX-XXX.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fus-central1-XXX.cloudfunctions.net%2Foauth2callback
res.redirect(url);
});
Redirect url is set in the Google Console Developer:
When I call the url https://us-central1-XXX.cloudfunctions.net/hello, I got "Error: could not handle the request" and "finished with status: 'timeout'" in the Firebase logs.
What's wrong?
I found a solution.
Full code using JWT to authenticate, then get the list of app's reviews:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase);
var {google} = require('googleapis');
const serviceAccount = require('./client_secret.json');
const { JWT } = require('google-auth-library');
const getAuthorizedClient = () => new JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: ['https://www.googleapis.com/auth/androidpublisher']
});
const getAndroidpublisher = () => google.androidpublisher({
version: 'v3',
auth: getAuthorizedClient()
});
const requestProductValidation = () => new Promise((resolve, reject) => {
getAndroidpublisher().reviews.list({
packageName: "com.my.packagename"
}, (err, response) => {
if (err) {
console.log(`The API returned an error: ${err}`);
resolve({status: "Error"});
} else {
return resolve(response);
}
});
});
exports.hello = functions.https.onRequest((req, res) => {
return requestProductValidation();
});
I'm in the process of creating a Discord bot that will read from a specific Google Sheets spreadsheet and this error keeps coming up as I am trying to integrate the Google Sheets functions. See Github Repo and please know I'm extremely new at Node.js.
For this function, I have created index.js to get everything running for both discord.js and the Google API:
require("dotenv").config();
const fs = require("fs");
const Discord = require("discord.js");
const client = new Discord.Client();
const readline = require('readline');
const { google } = require('googleapis');
const OAuth2Client = google.auth.OAuth2;
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
const TOKEN_PATH = 'token.json';
/**
* 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.
*/
const authorize = function (credentials, callback) {
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new OAuth2Client(client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getNewToken(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.
*/
const getNewToken = function (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 callback(err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
//pulls all commands and events
fs.readdir("./events/", (err, files) => {
files.forEach(file => {
const eventHandler = require(`./events/${file}`);
const eventName = file.split(".")[0];
client.on(eventName, (...args) => eventHandler(client, ...args));
});
});
client.login(process.env.BOT_TOKEN);
module.exports = {
authorize,
google
}
Sheets.js contains the readAll function for Sheets I want to use so when someone types in assignments!all the function should run:
const {authorize, google} = require('./index');
const fs = require("fs");
const readline = require('readline');
const Discord = require("discord.js");
const client = new Discord.Client();
const spreadsheetId = "1asvhCVI1sC6Q2cCuqdUrRqFHkH2VCr5FM7kWSm8VBE8";
//read entire spreadsheet
const readAll = (range) => {
fs.readFile('client_secret.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Sheets API.
authorize(JSON.parse(content), (auth) => {
const sheets = google.sheets({ version: 'v4', auth });
sheets.spreadsheets.values.get({
spreadsheetId, range
}, (err, res) => {
if (err) return console.log('The API returned an error:' + err);
const rows = res.data.values;
if (rows.length) {
// Print columns B through F.
rows.map((row) => {
message.reply(`Details: ${row[0]}, Link: ${row[1]}, Additional Link: ${row[2]}, Assigned To: ${row[3]}, Assigned On: ${row[4]}, Ready for QC on: ${row[5]}, QC Link: ${row[6]}`);
});
} else {
console.log('No data found.');
}
});
});
});
}
module.exports = {
readAll
};
As I plan to have the bot do more functions in the future, I have created a message.js file in an events folder to call commands from a commands folder:
const assignments = require('../commands/assignments');
const assign = require('../commands/assign');
const qc = require('../commands/qc');
const qcList = require('../commands/qcList');
const sheets = require('../sheets');
module.exports = (client, message) => {
if (message.content.startsWith('assignments!')) {
return assignments(message);
} else if (message.content.startsWith('assign!')) {
return assign(message);
} else if (message.content.startsWith('qc!')) {
return qc(message);
} else if (message.content.startsWith('qclist!')) {
return qcList(message);
}
};
Assignments.js actually tells the bot that when the word "all" is used after the ! in "assignments!", that is when it is suppose to run the ReadAll function on the specific tab in the Sheets spreadsheet:
// gets all assignments or assignments based on name given after !
module.exports = (message, google, authorize, readAll) => {
const sheets = require('../sheets');
//ex message: assignments!Brody
//gets name after ! and stores as member variable
var messageContent = message.content;
var member = messageContent.split('!')[1];
var member = member.toLowerCase();
//test reading spreadsheet
if(member === 'all') {
sheets.readAll("Active News Assignments!B2:F");
}
//error messages
if (!member) {
message.reply("You must add a name or 'all' after the command.");
}
};
Again, I am very new to Node.js and following these tutorials to create this bot. Please feel free point out anything obvious I'm missing or ways I can simplify my code once it's working as I know I will be adding more functions in the future.
https://levelup.gitconnected.com/making-a-custom-discord-bot-in-discord-js-1e17f2090919
https://www.ishaanrawat.com/integrate-google-sheets-api-with-node-js/
I'm trying to create a web app where a user can grant access to her Google Analytics account via OAuth2. After positive response I would like to make a request to that user's GA data (in the real application the request would be made "offline"). But when calling:
google.analytics('v3').data.ga.get(params, callback);
params should contain ids, which should be a list of "table ID"s from the user. How do I get hold of these IDs? Is it necessary to get this information through another profile-scoped-request?
Code:
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var clientId = '123-123.apps.googleusercontent.com';
var clientSecret = 'abc';
var redirectUrl = 'http://localhost:8080/redirect';
var authRequest = function(req, res) {
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
var scopes = [ 'https://www.googleapis.com/auth/analytics.readonly' ],
params = { state: 'some-data', access_type: 'offline', scope: scopes };
var url = oauth2Client.generateAuthUrl(params);
res.redirect(301, url); // will return to "authResult()"
};
var _sampleAnalytics = function(req, res, oauthClient) {
var params = {
auth: oauthClient,
metrics: 'ga:visitors,ga:visits,ga:pageviews',
'start-date': '2015-06-01',
'end-date': '2015-06-30',
ids: ['ga:123456789'] // <== How to get this parameter?
};
google.analytics('v3').data.ga.get(params, function(err, response) {
// todo
});
};
var authResult = function (req, res) {
if (req.query.error) {
return handleError(res, req.query.error);
}
var oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl);
oauth2Client.getToken(code, function(err, tokens) {
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(err) {
return handleError(res, err);
} else {
oauth2Client.setCredentials(tokens);
_sampleAnalytics(req, res, oauth2Client);
}
});
};
Ok, that was simple. I just need to make another call to:
google.analytics('v3').management.accountSummaries.list(params, function(err, result) {
// ...
});
result will contain all the required information.