Funzione Cloud Functions per automatizzare l'importazione di dati CSV in Fogli Google

1. Introduzione

Lo scopo di questo codelab è farti capire come scrivere una funzione Cloud per reagire al caricamento di un file CSV in Cloud Storage, per leggerne il contenuto e utilizzarlo per aggiornare un foglio Google utilizzando l'API Sheets.

e9c78061022a6760.png

Può essere considerato come l'automazione di un passaggio di "importazione come CSV" altrimenti manuale. In questo modo, puoi analizzare i dati (magari prodotti da un altro team) in un foglio di lavoro non appena sono disponibili.

Ecco come appare l'implementazione :

52df703605ae4bd3.png

2. Configurazione e requisiti

Configurazione dell'ambiente autonomo

  1. Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai già un account Gmail o G Suite, devi crearne uno.

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

Ricorda l'ID progetto, un nome univoco tra tutti i progetti Google Cloud (il nome sopra è già stato utilizzato e non funzionerà per te, mi dispiace). In questo codelab verrà chiamato PROJECT_ID.

  1. Successivamente, dovrai abilitare la fatturazione in Cloud Console per utilizzare le risorse Google Cloud.

L'esecuzione di questo codelab non dovrebbe costare molto, se non nulla. Assicurati di seguire le istruzioni riportate nella sezione "Pulizia", che ti consiglia come arrestare le risorse in modo da non incorrere in addebiti oltre questo tutorial. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.

3. Crea e configura un foglio Google e abilita l'API

Innanzitutto, creiamo un nuovo documento Fogli (questo foglio può appartenere a qualsiasi utente). Una volta creato, ricorda il suo identificatore, che verrà utilizzato come variabile di ambiente per la funzione che scriveremo :

dd77d5fc1364ad3e.png

Dalla console GCP, attiva l'API Google Sheets nel progetto appena creato andando alla sezione "API e servizi" e poi a "Libreria API":

c64e2e98b8b55f16.png

Nella sezione "IAM e amministrazione ", vai a"Service account " e prendi nota dell'email del service account predefinito di App Engine. Deve essere nel formato your-project-id@appspot.gserviceaccount.com. Naturalmente, puoi anche creare un tuo account di servizio dedicato a questa azione.

6e279d7e07d4febf.png

Infine, concedi a questo account di servizio i privilegi di modifica del foglio di lavoro utilizzando il pulsante "Condividi":

c334062465ddf928.png

Con questa configurazione possiamo ora scrivere la nostra Cloud Function e configurarla per utilizzare questo service account. Sarà in grado di scrivere in questo documento del foglio di lavoro che abbiamo appena creato.

4. Crea un bucket di archiviazione

Creiamo il bucket che la nostra funzione cloud monitorerà per i nuovi file CSV.

Nella console, utilizza il menu a sinistra per andare a "Spazio di archiviazione"… :

2ddcb54423979d25.png

… e crea un nuovo bucket chiamato csv2sheet-POSTFIX (sostituisci POSTFIX con qualcosa di univoco) con tutte le altre impostazioni impostate sui valori predefiniti :

dd637080ade62e81.png

5. Crea la funzione Cloud

Ora possiamo creare una funzione Cloud chiamata csv2sheet che viene attivata al caricamento di file in un bucket Cloud Storage specifico. Il codice verrà scritto in Node.js 8 con funzioni asincrone utilizzando l'editor incorporato direttamente nella console Cloud :

6ee1a5ce63174ae8.png

Assicurati di impostare il trigger su "Cloud Storage" e di modificare il nome del bucket in quello che hai creato nel passaggio precedente.

Aggiorna anche l'entry point della funzione che stiamo per scrivere a csv2sheet :

446e7c7c992c2d8a.png

Ora modifica il corpo della funzione in :

  1. utilizzare le API Cloud Storage e Sheets
  2. contrassegna la funzione csv2sheet come async
  3. recupera fileName dai metadati dell'evento Cloud Storage e deriva un nome per il nuovo foglio che creeremo :
const {google} = require("googleapis");
const {Storage} = require("@google-cloud/storage")

exports.csv2sheet = async (data, context) => {
  var fileName = data.name;
  // basic check that this is a *.csv file, etc...
  if (!fileName.endsWith(".csv")) {
    console.log("Not a .csv file, ignoring.");
    return;
  }
  // define name of new sheet  
  const sheetName = fileName.slice(0, -4);

  // TODO!
};

L'utilizzo di async qui è necessario per utilizzare await, come vedremo tra poco.

Alcune opzioni importanti durante la creazione di questa funzione includono (fai clic sul link "Altro" nella parte inferiore dello schermo) :

  • Utilizza il menu a discesa per selezionare il service account descritto sopra.
  • Definisci una variabile di ambiente denominata SPREADSHEET_ID che deve corrispondere al documento del foglio che hai creato in precedenza :

fd22d1873bcb8c66.png

Come ultimo passaggio di configurazione, ecco i contenuti di package.json con le API Cloud Storage e Google Sheets come due dipendenze che utilizzeremo (utilizza la scheda PACKAGE.JSON dell'editor incorporato della console) :

{
    "name": "csv2sheet",
    "version": "0.0.42",
    "dependencies": {
        "googleapis": "^51.0.0",
        "@google-cloud/storage": "^5.0.1"
    }
}

Una volta configurato tutto come descritto, fai clic su "Crea". Dopo un breve minuto, la funzione dovrebbe essere creata e sottoposta a deployment.

6. Configura l'autenticazione e l'API Sheets

Prima di scrivere altro codice nella nostra funzione Cloud utilizzando l'editor incorporato, dobbiamo bloccare la creazione di un'API Google Client con gli ambiti Storage e Fogli corretti (ricorda, fa parte di una funzione async).

Nell'editor di funzioni della console, fai clic su "MODIFICA" e aggiungi il seguente codice al corpo della funzione csv2sheet :

// block on auth + getting the sheets API object
const auth = await google.auth.getClient({
  scopes: [
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/devstorage.read_only"
  ]
});

Da qui possiamo creare un client API Google Sheets :

const sheetsAPI = google.sheets({version: 'v4', auth});

7. Utilizza l'API Sheets per creare un foglio vuoto

Con un client API Sheets possiamo creare un nuovo semplice foglio nel nostro documento, ma prima di andare avanti, ecco una breve nota sul vocabolario:

  • un foglio di lavoro è il documento vero e proprio e viene fatto riferimento al suo identificatore (descritto sopra e visibile nell'URL del documento)
  • un foglio è una delle schede del documento e può essere fatto riferimento al suo nome (il nome della scheda) o a un identificatore generato al momento della creazione del foglio

Tenendo presente questo, ecco una funzione che utilizza il client API Sheets per creare un foglio vuoto in posizione 2 (in genere dopo il "Foglio1 " predefinito), con 26 colonne, 2000 righe e la prima riga bloccata (aggiungila alla funzione utilizzando l'editor incorporato) :

function addEmptySheet(sheetsAPI, sheetName) {
  return new Promise((resolve, reject) => {
    const emptySheetParams = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            addSheet: {
              properties: {
                title: sheetName,
                index: 1,
                gridProperties: {
                  rowCount: 2000,
                  columnCount: 26,
                  frozenRowCount: 1
                }
              }
            }
          }
        ]
      }
    };
    sheetsAPI.spreadsheets.batchUpdate( emptySheetParams, function(err, response) {
        if (err) {
          reject("The Sheets API returned an error: " + err);
        } else {
          const sheetId = response.data.replies[0].addSheet.properties.sheetId;
          console.log("Created empty sheet: " + sheetId);
          resolve(sheetId);
        }
      }
    );
  });
}

Tieni presente che, anziché codificare in modo permanente il riferimento al foglio di lavoro, ci affidiamo alla variabile di ambiente SPREADSHEET_ID creata in precedenza.

Dobbiamo ricordare il sheetId per le ulteriori richieste effettuate a questo foglio specifico. Inoltre, il nome del foglio deve essere univoco e la creazione non andrà a buon fine se esiste già un foglio chiamato sheetName.

La funzione batchUpdate nell'API Fogli è un modo comune per interagire con i documenti ed è descritta qui.

8. Leggere i dati da un file CSV di archiviazione

Ora che abbiamo un posto dove scaricare i dati, sviluppiamo ulteriormente la nostra funzione cloud nell'editor incorporato e utilizziamo l'API Cloud Storage per recuperare i dati effettivi dal file appena caricato e archiviarli in una stringa:

function readCSVContent(sheetsAPI, file, sheetName) {
  return new Promise((resolve, reject) => {
    const storage = new Storage();
    let fileContents = new Buffer('');
    storage.bucket(file.bucket).file(file.name).createReadStream()
    .on('error', function(err) {
      reject('The Storage API returned an error: ' + err);
    })
    .on('data', function(chunk) {
      fileContents = Buffer.concat([fileContents, chunk]);
    })  
    .on('end', function() {
      let content = fileContents.toString('utf8');
      console.log("CSV content read as string : " + content );
      resolve(content);
    });
  });
}

9. Compila il foglio appena creato

Ora è il momento di compilare il foglio che abbiamo creato utilizzando la stessa API client Sheets e i dati che abbiamo appena raccolto. Cogliamo l'occasione per aggiungere anche uno stile alle colonne del foglio (modificando le dimensioni del carattere della prima riga e mettendolo in grassetto) :

function populateAndStyle(sheetsAPI, theData, sheetId) {
  return new Promise((resolve, reject) => {
    // Using 'batchUpdate' allows for multiple 'requests' to be sent in a single batch.
    // Populate the sheet referenced by its ID with the data received (a CSV string)
    // Style: set first row font size to 11 and to Bold. Exercise left for the reader: resize columns
    const dataAndStyle = {
      spreadsheetId: process.env.SPREADSHEET_ID,
      resource: {
        requests: [
          {
            pasteData: {
              coordinate: {
                sheetId: sheetId,
                rowIndex: 0,
                columnIndex: 0
              },
              data: theData,
              delimiter: ","
            }
          },
          {
            repeatCell: {
              range: {
                sheetId: sheetId,
                startRowIndex: 0,
                endRowIndex: 1
              },
              cell: {
                userEnteredFormat: {
                  textFormat: {
                    fontSize: 11,
                    bold: true
                  }
                }
              },
              fields: "userEnteredFormat(textFormat)"
            }
          }       
        ]
      }
    };
        
    sheetsAPI.spreadsheets.batchUpdate(dataAndStyle, function(err, response) {
      if (err) {
        reject("The Sheets API returned an error: " + err);
      } else {
        console.log(sheetId + " sheet populated with " + theData.length + " rows and column style set.");
        resolve();
      }
    });    
  });
}

Questo codice deve essere aggiunto alla nostra funzione Cloud, che ora è completa al 99%.

Nota come i dati e lo stile vengono combinati come più requests in una singola chiamata API Sheets batchUpdate. In questo modo l'aggiornamento è più efficiente e atomico.

Tieni presente inoltre che definiamo un intervallo di modifica che corrisponde alle dimensioni del foglio che abbiamo creato. Ciò significa che i contenuti che superano le 26 colonne (il valore columnCount utilizzato durante la creazione del foglio) non verranno caricati con questo codice specifico.

Se tutto va bene, a questo punto puoi:

  1. salva la funzione aggiornata
  2. trascina un file CSV nel bucket
  3. visualizzare i dati corrispondenti nel foglio di lavoro.

10. Mettere insieme tutti i pezzi e testare il flusso

Le chiamate alle funzioni che abbiamo appena discusso possono essere effettuate come chiamate di blocco successive nella funzione csv2sheet originale:

  const sheetId = await addEmptySheet(sheetsAPI, sheetName);
  const theData = await readCSVContent(sheetsAPI, data, sheetName);
  await populateAndStyle(sheetsAPI, theData, sheetId);

Se hai bisogno del codice sorgente completo della funzione, è disponibile qui (probabilmente è più facile ottenerlo tutto in un unico set).

Una volta che tutto è a posto, carica un file CSV nel bucket corretto e guarda il foglio di lavoro aggiornarsi con un nuovo foglio contenente i contenuti del file. Ecco un file CSV di esempio se non ne hai uno a portata di mano.

1efae021942e64fa.png

Prova a caricare diversi file nel bucket per vedere cosa succede.

11. È tutto. È ora di smantellare l'infrastruttura

Scherzo, non c'è alcuna infrastruttura da smantellare, è stato tutto fatto in modalità serverless.

Se vuoi, puoi eliminare la funzione cloud e il bucket che hai creato o anche l'intero progetto.

12. Passaggi successivi

Questo codelab si conclude qui. Ti ha guidato nei passaggi per ascoltare i caricamenti in un bucket Cloud Storage in una funzione Cloud Functions per aggiornare un foglio Google utilizzando l'API appropriata.

Ecco alcuni passaggi successivi :

Se hai riscontrato problemi con questo codelab, non esitare a segnalarli utilizzando il link nell'angolo in basso a sinistra.

La tua opinione è molto importante per noi.