Firebase Cloud Function with Google Secrets Manager how to get the async/await to work

Viewed 2307

I have a little problem, my firebase cloud function completes before I get the API key from calling the Google Secret Manager API. The API key is important as it makes an API call to get data from an external server to store the result of the API call in Google Cloud Storage.

Here is my code,

'use strict';

// Request Data From A URL
var request = require('request');
var https = require('https');

// Var Firebase Functions
var functions = require('firebase-functions');
const admin = require('firebase-admin');

// Initalise App
admin.initializeApp();
// init firebase admin and get the default project id
const projectId = admin.instanceId().app.options.projectId

const util = require('util');
// Imports the Google Storage client library
const {Storage} = require('@google-cloud/storage');

// Import the Secret Manager client and instantiate it:
const {SecretManagerServiceClient} = require('@google-cloud/secret-manager');
const secretClient = new SecretManagerServiceClient();

// Setting Timeout in Seconds - default is 1 second
// The maximum value for timeoutSeconds is 540, or 9 minutes. Valid values for memory are:
// 128MB, 256MB, 512MB, 1GB, 2GB

const runtimeOpts = {
  timeoutSeconds: 300,
  memory: '512MB'
}

let apikey = '';

// From were the data comes
// 1 = Shoreserver
var shipid = '1';

// Get the current date 
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();

today = '0000' + '-' + '00' + '-' + '00';

// Creates a storage client
const storage = new Storage({
  projectId: projectId,
});

// Set Bucket Name
const bucket = storage.bucket('masterlog');

/**
 * Delete a file from a storage bucket and download a new file from remote location to store in the bucket
 */
exports.getEmilyAPItoStorage = functions
  .runWith(runtimeOpts)
  .region('europe-west1')
  .https.onRequest((req, res) => {

    // Get Secret
    ***(async () => { apikey = await getSecret() })***
    console.info(`ApiKey Secret: ${apikey}`);

    // First we want to delete the current file, the filename is always the same.
    // Delete files in the Bucket people
    bucket.deleteFiles({
      prefix: `people.json`
    })
    .catch( (err) => {
      console.log(`Failed to delete people.json`);
    });

    // Start of the requesting different tables
    // Table to get data from
    var apitable = 'people';

    // Set destination filename
    const people = bucket.file('people.json'); 

    var url = 'https://<URL>/api/' + shipid + '/' + apitable + '?apikey=' + apikey + '&syncdate=' + today;

    // Set the options to make the request
    var options = {
      url: url,
      strictSSL: false,
      secureProtocol: 'TLSv1_method'
    }

    // Make a request for the API and store the file in Storage
    request(options)
      .pipe(people
      .createWriteStream({sourceFormat: 'NEWLINE_DELIMITED_JSON'}))
      .on('finish',  function(error) {
      if (error) {
          console.log(error);
          res.status(500).send(error);
      } else {
          console.log( "- done!")
          res.status(200).send("OK");
      }
    });
    // End Function with status code 200


    // Set destination filename
    const agents = bucket.file('agents.json'); 

    // Table to get data from
    var apitable = 'ports';

    var url = 'https://emily.greenpeace.net/api/' + shipid + '/' + apitable + '?apikey=' + apikey + '&syncdate=' + today;

    // Set the options to make the request
    var options = {
      url: url,
      strictSSL: false,
      secureProtocol: 'TLSv1_method'
    }

    // Make a request for the API and store the file in Storage
    request(options)
      .pipe(agents
      .createWriteStream({sourceFormat: 'NEWLINE_DELIMITED_JSON'}))
      .on('finish',  function(error) {
      if (error) {
          console.log(error);
          res.status(500).send(error);
      } else {
          console.log( "- done!")
          res.status(200).send("OK");
      }
    });
    // End Function with status code 200 

    async function getSecret() {
        // Access the secret.
        const resource_name = 'projects/' + projectId + '/secrets/emilyapikey/versions/latest';
        let [version] = await secretClient.accessSecretVersion({name: resource_name})    
        console.info(`Found secret ${version.payload.data} with state ${version.state}`);
        apikey = version.payload.data;
        return apikey;
    }    
  });

I can get the API key from Google Secrets Manager in the function getSecret(), the API Key is not available when I make the API key to my server. My expectation is that the getSecret would complete before it executes the rest of the code.

If someone has an insight her of what I'm missing I really interested to hear from you.

1 Answers

If you want to use async/await in any funciton, that function has to be declared async:

exports.getEmilyAPItoStorage = functions
  .runWith(runtimeOpts)
  .region('europe-west1')
  .https.onRequest(async (req, res) => { ... })

Then you can await in the code in its body:

const apikey = await getSecret()
Related