Sails.js : Call a youtube service from a controller - javascript

I try to connect Youtube Data API with Node.js in Sails.js, but I've a problem with "fs.readFile" function.
When I launch service it returns "undefined".
Here is my code for YoutubeService :
module.exports = {
callYoutubeApi: function (req, res) {
// Load client secrets from a local file.
fs.readFile('api/services/client_secret.json', function processClientSecrets(err, content) {
if (err) {
sails.log('Error loading client secret file: ' + err);
return "error";
}
// Authorize a client with the loaded credentials, then call the YouTube API.
sails.log('123');
return YoutubeService.authorize(JSON.parse(content), YoutubeService.getChannel);
});
},
`/**
* 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.
*/
authorize: function (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) {
sails.log('tata');
getNewToken(oauth2Client, callback);
} else {
sails.log('tonton');
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.
*/
getNewToken: function (oauth2Client, callback) {
var authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
sails.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) {
sails.log('Error while trying to retrieve access token', err);
return;
}
sails.log('getNewToken');
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.
*/
storeToken: function (token) {
try {
fs.mkdirSync(TOKEN_DIR);
} catch (err) {
if (err.code != 'EEXIST') {
throw err;
}
}
fs.writeFile(TOKEN_PATH, JSON.stringify(token));
sails.log('Token stored to ' + TOKEN_PATH);
},
/**
* Lists the names and IDs of up to 10 files.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
getChannel: function (auth) {
var service = google.youtube('v3');
service.channels.list({
auth: auth,
part: 'snippet,contentDetails,statistics',
id: 'UCQznUf1SjfDqx65hX3zRDiA'
}, function (err, response) {
if (err) {
sails.log('The API returned an error: ' + err);
return;
}
var channels = response.items;
if (channels.length == 0) {
sails.log('No channel found.');
} else {
sails.log(channels[0]);
return channels[0];
}
});
}
}`
The call in the controller :
channelData = YoutubeService.callYoutubeApi();
sails.log(channelData);
The error :
debug: undefined
debug: 123
I think the call is not working and it can't read the "processClientSecrets" function.
If someone have an idea to help me thanks !

You've created an asynchronous service function, but you're calling it synchronously. If you're not sure what that means, do some Googling to teach yourself about "asynchronous programming with Node.js" -- it will make your life a lot easier!
In this case, you'll want to change your helper's function signature from:
callYoutubeApi: function (req, res) {
to:
callYoutubeApi: function (cb) {
The cb stands for "callback", and it's a function you pass in that gets called when your asynchronous code completes. Wherever you're currently calling return with a literal value, instead return a call to cb. The convention in Node is that the first argument to cb is any error that may have occurred, or else null or undefined. The second argument is the result of the asynchronous code. Putting it all together you get:
callYoutubeApi: function (cb) {
// Load client secrets from a local file.
fs.readFile('api/services/client_secret.json', function processClientSecrets(err, content) {
if (err) {
// Using "return" just prevents you from accidentally continuing in this function.
return cb(err);
}
// Authorize a client with the loaded credentials, then call the YouTube API.
// This assumes that "authorize" is synchronous call that returns a value.
var auth = YoutubeService.authorize(JSON.parse(content), YoutubeService.getChannel);
return cb(undefined, auth);
});
},
which you would call in your controller with:
YoutubeService.callYoutubeApi(function(err, channelData) {
if (err) {
return res.serverError(err);
}
// Now do something with channelData...
});

Related

Google Cloud Function Cors Error Only When Error is Thrown

Dealing with a google cloud function that receives and responds as expected when there are no errors, however IF the function throws an error, on the client-side (Chrome) I receive a CORS error. I can't figure out if the issue is with how I am handling CORS or if it is because of a misuse of throw new function.https.httpsError.
Unsure if its related, the thrown error appears to be thrown AFTER the execution of the function finishes (based on logs).
I have set the function to be available to allUsers in the console.
I am using the cloud console to edit the function.
I did try using cors package
cloud function:
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');
const { getAuth } = require('firebase-admin/auth');
const functions = require('firebase-functions');
const app = initializeApp();
const db = getFirestore();
exports.registerUser = (req, res) => {
let registerDetails = req.body.data;
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'GET, POST')
res.set('Access-Control-Allow-Headers', 'Content-Type, Accept')
res.set('Access-Control-Max-Age', '3600')
return res.status(204).send('')
}
return getAuth().createUser({
email: registerDetails.Email,
emailVerified: false,
password: registerDetails.Password,
displayName: registerDetails.DisplayName,
disabled: false,
}).then((user)=>{
const message = 'Registered user ' + registerDetails.DisplayName + '.'
console.log('Successfully created new user:', user.uid)
return res.status(200).json({data: message })
//ALL OF THIS PART WORKS JUST FINE
}).catch((error)=>{
console.log('Error creating new user:', error.errorInfo.message)
throw new functions.https.HttpsError("already-exists", error.errorInfo.message)
//IF AN ERROR HAPPENS I SEE A CORS ERROR CLIENT SIDE
})
};
Client Side Code:
const regUser = fbf.httpsCallable('RegisterUser');
regUser({
FirstName: $('#registerFirstName').val(),
LastName: $('#registerLastName').val(),
DisplayName: $('#publicName').val(),
Email: $('#regEmail').val(),
Password: $('#regPassword').val()
}).then((result)=>{
$('#regInputHide').hide();
$('#regResponse').show();
$('#submitNewReg').hide();
$('#regFuncResponse').text(result.data)
console.log(result.data)
}).catch((err)=>{
console.warn(err)
//THIS WILL LOG "Error: internal
// # etc."
//IN ADDITION TO THE CORS ERROR
})
You are mixing the APIs for a HTTP Event Cloud Function with a Callable Cloud Function.
You need to use one or the other or at least add in the code to format the response from your function in a way that httpsCallable can parse.
// Exporting/using a `(req: Request, res: Response) => any` function is a
// HTTP Event Cloud Function pattern
exports.registerUser = (req, res) => {
/* ... */
// Returning a Promise chain is a Callable Cloud Function pattern
return getAuth().createUser({
/* ... */
// sending a response is a HTTP Event Cloud Function pattern
return res.status(200).json({data: message })
/* ... */
// throwing HttpsError instances is a Callable Cloud Function pattern
throw new functions.https.HttpsError("already-exists", error.errorInfo.message)
/* ... */
}

Request body values not being picked up in firebase cloud function [duplicate]

When sending {"identifiant": "iJXB5E0PsoKq2XrU26q6"} to the below Cloud Function, I cannot get the identifiant value in the request body and it will always return PROBLEMAS NO REQUEST.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
const identifiant = req.body.identifiant;
if(identifiant) res.status(200).json('ok').end();
res.status(500).json('PROBLEMAS NO REQUEST').end();
});
Unlike a Callable function, the body of a request is not parsed automatically and needs to be parsed before you can use it.
In addition, json(...) will call end() internally so you don't need both. Also make sure that you don't call end(), send(), json(), etc. multiple times, as this will lead to errors.
const jsonParser = require('body-parser').json();
exports.meusCandidatos = functions.https.onRequest((req, res) => {
jsonParser(req, res, (err) => {
if (err) {
res.status(500).json({error: err.message});
return; // stop here
}
const identifiant = req.body.identifiant;
if (!identifiant) {
res.status(500).json({error: 'PROBLEMAS NO REQUEST'});
return; // stop here
}
// continue
res.status(200).json({ status: 'ok' });
})
});

Google Sheets Node.js Quickstart Guide: How to return the rows array returned from API into a variable

I'm using Google's Sheets Quickstart API for Nodejs. I have everything installed properly and the code runs fine.
The main function is at the end of the code, called listMajors(). It produces a two-dimensional array of rows. The code simply maps the rows array into a console.log() including a couple of items from each row.
I'm planning to use this API in a complex app I'm creating. I can change the spreadsheet ID and range and get the values from my sheet fine.
What I want to do is get this 'rows' array outside of the function and into a new variable. I want to do this because a large part of my code is going to be written using the rows values and I don't want to have to write all the code inside the listMajors() function.
My question is, is there a way I can make this listMajors() function return the two-d array into a variable that I can use in the global scope? I haven't been able to accomplish this. I tried to just set the function equal to a variable and add a return rows; statement at the end of the function. I got the error "ReferenceError: listMajors is not defined".
Here is the original code:
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.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';
// 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 Sheets API.
authorize(JSON.parse(content), listMajors);
});
/**
* 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 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.
*/
function getNewToken(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 while trying to retrieve 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);
});
});
}
/**
* Prints the names and majors of students in a sample spreadsheet:
* #see https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
* #param {google.auth.OAuth2} auth The authenticated Google OAuth client.
*/
function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
console.log('Name, Major:');
// Print columns A and E, which correspond to indices 0 and 4.
rows.map((row) => {
console.log(`${row[0]}, ${row[4]}`);
});
} else {
console.log('No data found.');
}
});
}
This was the change I was trying to make:
var data = function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
//console.log('Name, Major:');
// Print columns A and E, which correspond to indices 0 and 4.
//rows.map((row) => {
// console.log(`${row[0]}, ${row[4]}`);
//});
return rows;
} else {
console.log('No data found.');
}
});
}
console.log(data);
I believe I could write all my other functions in a different file, import them, and then just call them inside the listMajors() function. This is a solution to my problem, but what I really want to know is if there is a way different way to accomplish this.
You want to retrieve the values retrieved from sheets.spreadsheets.values.get() at other function.
You want to achieve this using googleapis with Node.js.
You have already been able to get and put values for Spreadsheet using Sheets API.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modified script:
In this case, as a test case, at first, main() is called. So at first, please modify as follows.
From:
authorize(JSON.parse(content), listMajors);
To:
authorize(JSON.parse(content), main);
And please add main() and modify listMajors() as follows.
main() and listMajors()
async function listMajors(auth) {
const sheets = google.sheets({ version: "v4", auth });
const res = await sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
});
return res.data.values;
}
async function main(auth) {
const data = await listMajors(auth);
console.log(data);
}
By above modification, when main() is run, you can see the values retrieved with sheets.spreadsheets.values.get() at data.
If I misunderstood your question and this was not the direction you want, I apologize.
is there a way I can make this listMajors() function return the two-d array into a variable that I can use in the global scope?
Yes and you have to create a variable outside the function declaration and then assign the function output to created variable.
Here is a step by step guide how to create Array
create majors[] array
add majors[] array values from columns Name and Majors
list majors[] values to see the whole data
list majors[1] values to see the name and major (Anna, English)
// 1
let majors = [];
function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
// 2
rows.map((row) => {
majors.push(`${row[0]}, ${row[4]}`);
});
} else {
console.log('No data found.');
}
// 3
console.log(majors);
console.log('--------------------')
// 4
console.log(majors[2])
});
}
This is the console.log() output:
[
'Alexandra, English', 'Andrew, Math',
'Anna, English', 'Becky, Art',
'Benjamin, English', 'Carl, Art',
'Carrie, English', 'Dorothy, Math',
'Dylan, Math', 'Edward, English',
'Ellen, Physics', 'Fiona, Art',
'John, Physics', 'Jonathan, Math',
'Joseph, English', 'Josephine, Math',
'Karen, English', 'Kevin, Physics',
'Lisa, Art', 'Mary, Physics',
'Maureen, Physics', 'Nick, Art',
'Olivia, Physics', 'Pamela, Math',
'Patrick, Art', 'Robert, English',
'Sean, Physics', 'Stacy, Math',
'Thomas, Art', 'Will, Math'
]
--------------------
Anna, English
From now you can use your majors[] array how ever you want.
If you want to have more control over your data, you can create major{} object
Almost same steps as above:
create data{} object
add to data{} object all values
list data{} values to see the whole data
create listData() function for easier row output
list Anna in data{} which is located in row 4 in Google Sheets, but we avoid the first row, which means -1, so in data{} her index is 2.
// 1
let data = {
names : [],
gender : [],
classLevel : [],
homeState : [],
major : [],
extracurricularActivity : []
}
// 4
function listData(row){
console.log(data.names[row]);
console.log(data.gender[row]);
console.log(data.classLevel[row]);
console.log(data.homeState[row]);
console.log(data.major[row]);
console.log(data.extracurricularActivity[row]);
}
function listMajors(auth) {
const sheets = google.sheets({version: 'v4', auth});
sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:F',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const rows = res.data.values;
if (rows.length) {
// 2
rows.map((row) => {
data.names.push(row[0]);
data.gender.push(row[1]);
data.classLevel.push(row[2]);
data.homeState.push(row[3]);
data.major.push(row[4]);
data.extracurricularActivity.push(row[5]);
});
} else {
console.log('No data found.');
}
// 3
console.log(data);
console.log('--------------------')
// 5
listData(2);
});
}
Here you can see an output for listData(2):
Anna
Female
1. Freshman
NC
English
Basketball
There are more advanced ways to do this, take a look at reference.
I hope these examples will help you to start your Google Sheets API with Node.js
Reference:
Google Sheets API - Node.js Quickstart
spreadsheets.values.get
How can I create a two dimensional array in JavaScript?
You may look at my question ( I have added the workable solution in the end of the question)
I don't claim it to be the perfect solution as I m still working on understanding async, callback, promise in javascript however I have searched the internet and using row variable outside listmajor function was something I wanted to achieve and I did it with my code.
Nodejs And Google spreadsheet Integration ( Cannot read property 'length' of undefined )

Auth code failing when requiring data from another js file. But works when inside same file - why?

Still an unsolved mystery. Anyone who finds the answer will now save my project!
I have an app which requires credentials from Spotify. I am using a template (https://github.com/firebase/functions-samples/tree/master/spotify-auth) to sign in via Spotify but I am changing it so that the spotify client id and client secret credentials are retrieved from the Firestore database.
In order to save the credentials for use throughout the main index.js file, I am exporting the data via a spotify.js file and storing that within a const in the index.js file.
Everything works here apart from the exports.token function: this is the function that stores the auth code (from the address bar) and then uses the auth code in the authorisationCodeGrant. This eventually gets an access token and a refresh token. (see popup.html below for how this looks in the code. None of this was changed but it could be the reason for the problem)
For some reason, the auth code which is used comes back as 'invalid' on the authorisationCodeGrant and this means I don't get the access nor refresh tokens back. See code below:
index.js
'use strict';
const functions = require('firebase-functions');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
// Firebase Setup
const admin = require('firebase-admin');
const serviceAccount = require('./service-account.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,
});
// Spotify OAuth 2 setup
const spotify = require('./spotify');
// TODO: Configure the `spotify.client_id` and `spotify.client_secret` Google Cloud environment variables.
const SpotifyWebApi = require('spotify-web-api-node');
// Scopes to request.
const OAUTH_SCOPES = ['user-read-email'];
/**
* Redirects the User to the Spotify authentication consent screen. Also the 'state' cookie is set for later state
* verification.
*/
exports.redirect = functions.https.onRequest((req, res) => {
cookieParser()(req, res, () => {
const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
console.log('Setting verification state:', state);
res.cookie('state', state.toString(), {maxAge: 3600000, secure: true, httpOnly: true});
spotify.then(Spotify =>{
console.log('This is the client id used in redirect ' + Spotify.getClientId());
console.log('This is the client secret used in redirect ' + Spotify.getClientSecret());
const authorizeURL = Spotify.createAuthorizeURL(OAUTH_SCOPES, state.toString());
res.redirect(authorizeURL);
})
});
});
/**
* Exchanges a given Spotify auth code passed in the 'code' URL query parameter for a Firebase auth token.
* The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.
* The Firebase custom auth token is sent back in a JSONP callback function with function name defined by the
* 'callback' query parameter.
*/
exports.token = functions.https.onRequest((req, res) => {
try {
cookieParser()(req, res, () => {
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
spotify.then(Spotify =>{
Spotify.authorizationCodeGrant(req.query.code, (error, data) => {
if (error) {
throw error;
}
console.log('Received Access Token:', data.body['access_token']);
Spotify.setAccessToken(data.body['access_token']);
Spotify.getMe(async (error, userResults) => {
if (error) {
throw error;
}
console.log('Auth code exchange result received:', userResults);
// We have a Spotify access token and the user identity now.
const accessToken = data.body['access_token'];
const spotifyUserID = userResults.body['id'];
const profilePic = userResults.body['images'][0]['url'];
const userName = userResults.body['display_name'];
const email = userResults.body['email'];
// Create a Firebase account and get the Custom Auth Token.
const firebaseToken = await createFirebaseAccount(spotifyUserID, userName, profilePic, email, accessToken);
// Serve an HTML page that signs the user in and updates the user profile.
res.jsonp({token: firebaseToken});
});
});
})
});
} catch (error) {
return res.jsonp({error: error.toString});
}
return null;
});
/**
* Creates a Firebase account with the given user profile and returns a custom auth token allowing
* signing-in this account.
* Also saves the accessToken to the datastore at /spotifyAccessToken/$uid
*
* #returns {Promise<string>} The Firebase custom auth token in a promise.
*/
async function createFirebaseAccount(spotifyID, displayName, photoURL, email, accessToken) {
// The UID we'll assign to the user.
const uid = `spotify:${spotifyID}`;
// Save the access token to the Firebase Realtime Database.
const databaseTask = admin.database().ref(`/spotifyAccessToken/${uid}`).set(accessToken);
// Create or update the user account.
const userCreationTask = admin.auth().updateUser(uid, {
displayName: displayName,
photoURL: photoURL,
email: email,
emailVerified: true,
}).catch((error) => {
// If user does not exists we create it.
if (error.code === 'auth/user-not-found') {
return admin.auth().createUser({
uid: uid,
displayName: displayName,
photoURL: photoURL,
email: email,
emailVerified: true,
});
}
throw error;
});
// Wait for all async tasks to complete, then generate and return a custom auth token.
await Promise.all([userCreationTask, databaseTask]);
// Create a Firebase custom auth token.
const token = await admin.auth().createCustomToken(uid);
console.log('Created Custom token for UID "', uid, '" Token:', token);
return token;
}
spotify.js
const SpotifyWebApi = require('spotify-web-api-node');
// Firebase Setup
const admin = require('firebase-admin');
const serviceAccount = require('./service-account.json');
function regexIdAndSecret(clientIdOrSecret) {
const regex = /[(\w)]+/g;
let n;
let match;
while ((n = regex.exec(clientIdOrSecret)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (n.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `n`-variable.
n.forEach((match, groupIndex) => {
console.log(`Found match, group ${groupIndex}: ${match}`);
});
console.log(`Found n, ${n}`);
return n;
}
}
class Credentials {
constructor(client_id, client_secret) {
this.client_id = client_id;
console.log('Id in class ' + this.client_id);
this.client_secret = client_secret;
console.log('Secret in class ' + this.client_secret);
}
}
module.exports = admin.firestore().collection('credentials').doc('marc.jwatts#gmail.com').get().then((snapshot) => {
let client_id = JSON.stringify(snapshot.data().client_id);
let client_secret = JSON.stringify(snapshot.data().client_secret);
// console.log(JSON.stringify(doc.data().client_id));
// Credentials.client_id = JSON.stringify(doc.data().client_id);
// console.log(JSON.stringify(doc.data().client_secret));
// Credentials.client_secret = JSON.stringify(doc.data().client_secret);
const credentials = new Credentials(regexIdAndSecret(client_id), regexIdAndSecret(client_secret));
const Spotify = new SpotifyWebApi({
client_id: credentials.client_id,
client_secret: credentials.client_secret,
redirectUri: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`
});
Spotify.setClientId(credentials.client_id);
Spotify.setClientSecret(credentials.client_secret);
console.log('This is the client id after it has been set ' + credentials.client_id);
console.log('This is the client secret after it has been set ' + credentials.client_secret);
return Spotify;
});
As I mention, this problem does not occur when the details are in the same file. Does anyone know why the cookie parser part could be breaking due to data being from another file? What's even more strange is that the authorisation codes do indeed work perfectly well - I have tested these on Postman. But they come back as invalid in the code - no idea why! I thought it might be whitespace issue but this isn't it either, any ideas anyone? Got this project in tomorrow night so getting pretty frantic at this point...
Below is the popup.html which invokes the redirect (working) and token (not working) functions:
<!doctype html>
<!--
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="Demonstrates how to authorize Firebase with Spotify auth using Firebase Functions">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Authenticate with Spotify</title>
</head>
<body>
Please wait...
<!-- Firebase -->
<script src="/__/firebase/6.4.1/firebase-app.js"></script>
<script src="/__/firebase/6.4.1/firebase-auth.js"></script>
<script src="/__/firebase/init.js"></script>
<script>
/**
* Returns the value of the given URL query parameter.
*/
function getURLParameter(name) {
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||
[null, ''])[1].replace(/\+/g, '%20')) || null;
}
/**
* Returns the ID of the Firebase project.
*/
function getFirebaseProjectId() {
return firebase.app().options.authDomain.split('.')[0];
}
/**
* This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.
*/
function tokenReceived(data) {
if (data.token) {
firebase.auth().signInWithCustomToken(data.token).then(function() {
window.close();
});
} else {
console.error(data);
document.body.innerText = 'Error in the token Function: ' + data.error;
}
}
var code = getURLParameter('code');
var state = getURLParameter('state');
var error = getURLParameter('error');
if (error) {
document.body.innerText = 'Error back from the Spotify auth page: ' + error;
} else if(!code) {
// Start the auth flow.
window.location.href = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/redirect';
} else {
// Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.
const script = document.createElement('script');
script.type = 'text/javascript';
// This is the URL to the HTTP triggered 'token' Firebase Function.
// See https://firebase.google.com/docs/functions.
var tokenFunctionURL = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/token';
script.src = tokenFunctionURL +
'?code=' + encodeURIComponent(code) +
'&state=' + encodeURIComponent(state) +
'&callback=' + tokenReceived.name;
document.head.appendChild(script);
}
</script>
</body>
</html>

Gmail API node js - gapi not defined

I am using the following function from Google Gmail APIs to get all messages from users
/**
* 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, []);
}
I get the following error:
var initialRequest = gapi.client.gmail.users.messages.list({
^
ReferenceError: gapi is not defined
I tried replacing gapi with google since I have initialized var google for api module but that didn't work. Where am I going wrong?
I was facing the same problem..
GAPI is used for Client-side javascript only, not server side with Nodejs.
1) After you set up the sample from Node.js Quickstart
https://developers.google.com/gmail/api/quickstart/nodejs
2) Try to use this function to List and Get the recent email from your account:
function getRecentEmail(auth) {
const gmail = google.gmail({ version: 'v1', auth });
gmail.users.messages.list({ auth: auth, userId: 'me', maxResults: 1, q: 'subject:()' }, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
// Get the message id which we will need to retreive tha actual message next.
var message_id = response['data']['messages'][0]['id'];
// Retreive the actual message using the message id
gmail.users.messages.get({ auth: auth, userId: 'me', 'id': message_id }, function (err, response) {
if (err) {
console.log('The API returned an error: ' + err);
return;
}
// console.log(response['data']);
var to = response['data']['payload']['headers'][4].value;
var from = response['data']['payload']['headers'][5].value;
var subject = response['data']['payload']['headers'][6].value;
var email_body_raw = response['data']['payload']['body']['data'];
// Convert encoded email body message into human readable text message
data = email_body_raw;
buff = new Buffer.from(data, 'base64');
email_body_readable = buff.toString();
console.log('content:\n' + email_body_readable + '\n__________\nto: ' + to + '\nfrom: ' + from + '\nsubject: ' + subject + '\n__________\n');
});
});
}
fs.readFile('credentials.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
//authorize(JSON.parse(content), sendMessage);
authorize(JSON.parse(content), getRecentEmail);
});
https://developers.google.com/gmail/api/v1/reference/users/messages/list
https://developers.google.com/gmail/api/v1/reference/users/messages/get
Hope this helps!

Categories

Resources