Функция Firebase Callable (NOde.js) с API REST Google Sheets и аутентификацией OAuth2 - PullRequest
0 голосов
/ 18 декабря 2018

Я хотел бы получить значения из электронной таблицы Google, используя REST API Google Sheets (googleapis) с функциями, вызываемыми Firebase.Я нашел пример , но хочу переписать его с помощью Promises. Не могли бы вы помочь мне понять, что не так с этим кодом, или, возможно, возможно ли получить новый токен доступа (или токен обновления) без использования библиотеки ReadLine?Извините за мой грязный код, я хотел войти все шаги, чтобы найти проблему ...

На основе примера: https://developers.google.com/gmail/api/quickstart/nodejs

'use strict'
const functions = require('firebase-functions'); //MUST BE HERE!

const fs = require('fs');
const readline = require('readline');
const { google } = require('googleapis');

const SCOPES = require("./functions/gapi-auth2/SCOPES.js");

//Go to the Google CLoud Dashboard: APIs and Services/ Credentials panel: 
//https://console.cloud.google.com/apis/credentials
//OAuth2 Client Credentials (Other):
const KEY_PATH = "functions/credentials/oauth2.key.json"; // pathToKey

/**
 * Read content of the oauth2.key.json key file
 * @param {String} pathToKey The path to oauth2.key.json file.
 */
function readContentOfKeyFile(pathToKey) {
  console.log("Inside readContentOfKeyFile() -------------------------- pathToKey: \n");
  console.log(pathToKey);
  return new Promise((resolve, reject) => {
    console.log("Before readFile --------------------");
    // Load client secrets from a local file.
    fs.readFile(pathToKey, (err, content) => {
      console.log("Inside readFile() --------------------");
      if (err) {
        console.log('Error loading client secret file:', err);
        reject(err)
      }
      console.log("End of readContentOfKeyFile() --------------------");
      console.log(JSON.parse(content));
      resolve(JSON.parse(content));
    });
  })
}

/**
 * Create an OAuth2 client with the given credentials
 * @param {Object} credentials The authorization client credentials.
 */
function getOAuth2Client(credentials) {
  console.log("Inside getOAuth2Client() --------------------------");
  const { client_secret, client_id, redirect_uris } = credentials.installed;
  console.log("After credentials.installed, returns oAuth2Client --------------------------");
  //return oAuth2Client:
  return new google.auth.OAuth2(
    client_id,
    client_secret,
    redirect_uris[0]
  );
}

/**
 * Store token to disk be used in later program executions.
 *
 * @param {Object} token The token to store to disk.
 */
function storeToken(token) {
  fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
    if (err) return console.warn(`Token not stored to ${TOKEN_PATH}`, err);
    console.log(`Token stored to ${TOKEN_PATH}`);
  });
}



function getNewToken(oauth2Client, callback) {
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });

  console.log('Authorize this app by visiting this url:', authUrl);

  function askCode() {
    console.log("Inside askCode()----------------------------------")
    return new Promise((resolve) => {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });
      console.log("After readline.createInterface() ----------------------------------")
      rl.question('Enter the code from that page here: ', (code) => {
        rl.close();
        console.log(code);
        console.log("----------------------------------------------")
        resolve(code)
      })
    })
  }

  askCode().then(code => {
    oauth2Client.getToken(code, (err, token) => {
      return new Promise((resolve, reject) => {
        if (err) {
          console.error('Error retrieving access token', err);
          reject(err);
        }
        oauth2Client.credentials = token;
        storeToken(token);
        onsole.error("After storeToken()-------------------");
        resolve(oauth2Client);
      })
    });
  })

}

/**
* Get and store new token after prompting for user authorization
* @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
*/
function getNewToken(oAuth2Client, tokenFile) {
  console.log("Inside getNewToken() --------------------------");
  return new Promise((resolve, reject) => {

    const authUrl = oAuth2Client.generateAuthUrl({
      access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
      scope: SCOPES,
    });

    console.log('Authorize this app by visiting this url:', authUrl);

    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });

    console.log("readLine interface.........................."); //LAST RECORD IN THE LOG
    try {
      rl.question('Enter the code from that page here: ', (code) => {
        rl.close();
        console.log("rl.question: get code " + code);
        oAuth2Client.getToken(code, (err, token) => {
          if (err) {
            console.error('Error retrieving access token', err);
            reject(err);
          }
          console.error('token:' + token);
          oAuth2Client.credentials = token; //oAuth2Client.setCredentials(token);

          // Store the token to disk for later program executions
          storeToken(token);

          console.error("[END] of getNewToken()");
          resolve(oAuth2Client);
        });
      });
    } catch (err) {
      console.log("Error in the readLine......");
      console.error(err);
      reject(err);
    };


  })
}


// 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';

function authorize(client, tokenFile) {
  console.log("Inside authorize() --------------------------");
  return new Promise((resolve, reject) => {
    // Check if we have previously stored a token.
    fs.readFile(tokenFile, (err, token) => {
      if (err) {
        console.log("Error reading a file: " + err);
        try {
          resolve(getNewToken(client, tokenFile));
        } catch (err) {
          reject(err)
        }
      } else {
        console.log("set Credentials from a file--------------------")
        client.credentials = token; //client.setCredentials(JSON.parse(token));
        resolve(client)
      }
    });
  })
}


function getSpreadsheetData(auth) {
  console.log("Inside getSpreadsheetData--------------------------");
  return new Promise((resolve, reject) => {
    const sheets = google.sheets({ version: 'v4' });

    const request = {
      auth: auth,
      spreadsheetId: 'xxxx-xxxx-xxx', 
      range: "Checklist!B:L", 
    }

    console.log("****************************************************************************");
    console.log("***** getSpreadsheetData, request:------------------------------------------------");
    console.log(JSON.stringify(request));
    console.log("****************************************************************************");

    sheets.spreadsheets.values.get(request, (err, response) => {
      //console.log("inside: sheets.spreadsheets.values.get() -------------------------------");

      if (err) {
        console.log('The Sheets API returned an error: ' + err);
        //The API returned an error: Error: API key not valid. Please pass a valid API key.
        reject(err);
      };

      try {
        var numRows = response.data.values ? response.data.values.length : 0;
        console.log('%d rows retrieved.', numRows);

        console.log("response.data:-------------------------------");
        console.log(response.data.values);

        resolve(response.data.values);

      } catch (err) {
        console.log("Error processing Sheets API response: " + err);
        reject(err);
      }

    })

  })
}

//https://firebase.google.com/docs/functions/callable
exports.getControlSheetData = functions.https.onCall((data, context) => {
  console.log("getControlSheetData()---------------------------");
  if (!context.auth) {
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' + 'while authenticated.');
  } else {
    const uid = context.auth.uid;
    console.log("context.auth.uid: " + uid);

    const email = context.auth.token.email || null;
    console.log("--- * * * * * * * * * * * * * * * * * * ---");
    console.log("context.auth.token.email: ", email);
    console.log("--- * * * * * * * * * * * * * * * * * * ---");

    readContentOfKeyFile(KEY_PATH).then(credentials => {
      console.log("Result of readContentOfKeyFile() --------------------------");
      let oAuth2Client = getOAuth2Client(credentials);
      console.log("oAuth2Client has been created -----------------------");
      authorize(oAuth2Client, TOKEN_PATH).then(auth => {
        console.log("result of authorize(): " + JSON.stringify(auth));
        return getSpreadsheetData(auth); //<------------ Requested Spreadsheet's Data
      }).catch(err => {
        console.log("error in authorize: " + err);
      })

    })

  }
})

ReadLine Interface - function's log

...