0

I am somewhat new to coding and recently created this script in order to pull data from Zoom and push it to Google Drive via API. I am trying to push this to a Google Cloud Function, but when running it in a Cloud Function and console logging each step in the process, it seems like the uploadFile function, specifically the drive.files.create method, is being skipped. Every other step is being console logged, but neither the err or res is being logged after the drive.files.create method. Google Cloud Functions does not show errors, instead it shows OK and that the function took 1500ms to execute. It works fine on my local machine, I am only having issues in Cloud Functions. Any suggestions on how to get this to act right would be super helpful. Thank you!

const axios = require("axios");
require("dotenv").config();
const stream = require("stream");
const request = require("request");
const { google } = require("googleapis");

const KEYFILEPATH = "./credentials.json";
const SCOPES = ["https://www.googleapis.com/auth/drive"];

const auth = new google.auth.GoogleAuth({
  keyFile: KEYFILEPATH,
  scopes: SCOPES,
});

let today = new Date().toISOString();

let zoomAccessToken;
let zoomDownloadUrl;

///////////////////////////////////////////////////////////////// Searching for latest Town Hall recording in Google.
const searchFile = async (auth) => {
  const service = google.drive({ version: "v3", auth });
  const files = [];
  try {
    const res = await service.files.list({
      corpora: "drive",
      includeItemsFromAllDrives: true,
      supportsAllDrives: true,
      driveId: "XXXXXXXXXXXXXXXX",
      q: '"XXXXXXXXXXXXXXX" in parents',
      fields: "nextPageToken, files(id, name)",
      spaces: "drive",
    });
    Array.prototype.push.apply(files, res.files);
    const filesArray = res.data.files;
    const filesName = filesArray.map((x) => x.name).sort().reverse()[0];
    console.log(filesName);
    return filesName;
  } catch (err) {
    throw err;
  }
};

///////////////////////////////////////////////////////////////// Get Zoom OAuth access token.
const getAccessToken = async () => {
  return axios({
    method: "post",
    url: `https://zoom.us/oauth/token?grant_type=account_credentials&account_id=${process.env.ZOOM_ACCOUNT_ID}`,
    headers: {
      Authorization: "Basic" +new Buffer.from(process.env.ZOOM_CLIENT_ID + ":" + process.env.ZOOM_CLIENT_SECRET).toString("base64"),
    },
  });
};

///////////////////////////////////////////////////////////////// Get the latest Town Hall recording's data.
const getRecordingData = async () => {
  const token = await getAccessToken();

  zoomAccessToken = await token.data.access_token;

  return axios({
    method: "get",
    url: "https://api.zoom.us/v2/meetings/XXXXXXXXX/recordings",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${zoomAccessToken}`,
    },
  });
};

///////////////////////////////////////////////////////////////// Get the latest Town Hall recording's date.
const getRecordingDate = async () => {
  const recording = await getRecordingData();
  const lastRecordingDate = await recording.data.start_time;
  const recordingDateFormatted = `${lastRecordingDate.substring(0,4)}.${lastRecordingDate.substring(5, 7)}.${lastRecordingDate.substring(8,10)} - Town Hall.mp4`;
  return recordingDateFormatted;
};

///////////////////////////////////////////////////////////////// Get the latest Town Hall recording's download link.
const zoomDownloadLink = async () => {
  const recording = await getRecordingData();
  zoomDownloadUrl = `${recording.data.recording_files[0].download_url}?access_token=${zoomAccessToken}`;
  return zoomDownloadUrl;
};

///////////////////////////////////////////////////////////////// Upload data from latest Town Hall recording's download link to Google Drive.
const uploadFile = async (auth) => {
  const buffer = await zoomDownloadLink();
  const bs = new stream.PassThrough();
  request(buffer).pipe(bs);

  const drive = google.drive({ version: "v3", auth });
  var fileMetadata = {
    name: `${today.substring(0, 4)}.${today.substring(5, 7)}.${today.substring(8,10)} - Town Hall.mp4`,
    parents: ["XXXXXXXXXXXXXXXXX"],
  };
  var media = {
    mimeType: "video/mp4",
    body: bs,
  };
  drive.files.create(
    {
      resource: fileMetadata,
      media: media,
      fields: "id",
      uploadType: "resumable",
      supportsAllDrives: true,
    },
    function (err, res) {
      if (err) {
        console.log(err);
      } else {
        console.log("File Id: ", res.data.id);
      }
    }
  );
};

///////////////////////////////////////////////////////////////// Compares Town Hall files in Google Drive and Zoom. If different, run uploadFile function.
exports.townHall = async () => {
  const townHallFile = await searchFile(auth);
  const lastRecordingDate = await getRecordingDate();

  if (townHallFile != lastRecordingDate) {
    await uploadFile(auth);
  } else {
    console.log("No Recording Today");
  }
};
4
  • 1
    Could you edit the question to be more specific about what's happening that's different than what you expect? "after the first return" isn't very clear. I'd suggest adding some logging and share that output so we can all better see what happens. Commented Aug 15, 2022 at 21:53
  • @DougStevenson after console logging each step in the process, it seems like the uploadFile function, specifically the drive.files.create method, is being skipped. Every other step is being console logged, but neither the err or res is being logged after the drive.files.create method. Google Cloud Functions does not show errors, instead it shows OK and that the function took 1500ms to execute. Commented Aug 16, 2022 at 13:37
  • It's a good idea to edit the question to include all of your debugging details (don't leave important things in comments, as they are more likely to be missed). Commented Aug 16, 2022 at 13:53
  • @DougStevenson, thank you. I have updated the main question. Commented Aug 16, 2022 at 14:12

1 Answer 1

1

As you are calling an API inside a cloud function which is an async function but does not have a return statement, it will only execute the function but doesn't wait for the response, because the drive.files.create call is running.

So to fix that just need to await the result of the API. Just add

return await statement on the API call

like:

const uploadFile = async (auth) => {
  const buffer = await zoomDownloadLink();
  const bs = new stream.PassThrough();
  request(buffer).pipe(bs);

  const drive = google.drive({ version: "v3", auth });
  var fileMetadata = {
    name: `${today.substring(0, 4)}.${today.substring(5, 7)}.${today.substring(8,10)} - Town Hall.mp4`,
    parents: ["XXXXXXXXXXXXXXXXX"],
  };
  var media = {
    mimeType: "video/mp4",
    body: bs,
  };
  return await drive.files.create(
    {
      resource: fileMetadata,
      media: media,
      fields: "id",
      uploadType: "resumable",
      supportsAllDrives: true,
    },
    function (err, res) {
      if (err) {
        console.log(err);
      } else {
        console.log("File Id: ", res.data.id);
      }
    }
  );
};

Also, something important when you are calling APIs inside cloud functions is the time out. Check on your CF time out is enough to wait for the API call response.

Also, you can use the Promise function to force wait for the response:

const result = uploadFile(aut);
const _response = await Promise.all(result);
Sign up to request clarification or add additional context in comments.

11 Comments

Thanks for your suggestion. I have my CF time out set to 540 seconds (9 minutes). The function seems to end in 1500ms. I added the "return await drive.files.create" but VScode shows that the await is unnecessary. I also added console.log's to each function and they print out in the proper order. It just seems to be the uploadFile function that skips the actual upload part. I don't receive the console.log err or res after the uploadFile function either.
Yes is the same, I meant, is not that is skipping, is just that is not waiting because you are not returning anything, you could change instead of return await, just return, but add the return.
I tried return too, but it still skips over the function since I am not receiving a console log of res or err
Yes is not returning anything because the call is dying before the request gets completed, could try to change why I just put it, I edited the comment, Is basically doing the same but it is a way to make it work in CF
with the Promise.all addition I am receiving "Error: function execution failed. Details: Internal Server Error"
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.