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

1. Introduzione

L'obiettivo di questo codelab è comprendere come scrivere una funzione Cloud Functions per reagire al caricamento di un file CSV su Cloud Storage, leggerne i contenuti e utilizzarlo per aggiornare un foglio Google mediante l'API Fogli.

e9c78061022a6760.png

Può essere visto come l'automazione di un'operazione manuale di importazione in formato CSV passaggio. In questo modo potrai analizzare i dati (magari prodotti da un altro team) in un foglio di lavoro non appena sono disponibili.

Ecco come si presenta l'implementazione :

52df703605ae4bd3.png

2. Configurazione e requisiti

Configurazione dell'ambiente da seguire in modo autonomo

  1. Accedi alla console Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora 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 in tutti i progetti Google Cloud (il nome precedente è già stato utilizzato e non funzionerà correttamente). Verrà indicato più avanti in questo codelab come PROJECT_ID.

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

Eseguire questo codelab non dovrebbe costare molto. Assicurati di seguire le istruzioni nella sezione "Pulizia" in cui viene spiegato come arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

3. Creare e configurare un foglio Google e abilitare l'API

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

dd77d5fc1364ad3e.png

Nella console di Google Cloud, per attivare l'API Fogli Google sul progetto appena creato, vai alla sezione "API e servizi" quindi su "Libreria API" sezione :

c64e2e98b8b55f16.png

Nella sezione "IAM e amministratore" vai ad "Account di servizio" e prendi nota dell'email per l'account di servizio predefinito di App Engine. Deve avere il formato your-project-id@appspot.gserviceaccount.com. Naturalmente puoi anche creare un account di servizio dedicato a questa azione.

6e279d7e07d4febf.png

Infine, concedi semplicemente all'account di servizio i privilegi di modifica per il tuo foglio di lavoro utilizzando il pulsante "Condividi" :

c334062465ddf928.png

Con questa configurazione ora possiamo scrivere la funzione Cloud Functions e configurarla per l'utilizzo di questo account di servizio. Potrà scrivere in questo foglio di lavoro che abbiamo appena creato.

4. Crea un bucket di archiviazione

Creiamo il bucket che la nostra funzione Cloud Functions monitorerà per rilevare i nuovi file CSV.

Nella console, utilizza il menu a sinistra per passare a "Spazio di archiviazione"... :

2ddcb54423979d25.png

... e creiamo un nuovo bucket chiamato csv2sheet-POSTFIX (sostituisci POSTFIX con qualcosa di univoco) con tutte le altre impostazioni configurate sui valori predefiniti :

dd637080ade62e81.png

5. crea la funzione Cloud Functions

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

6ee1a5ce63174ae8.png

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

Aggiorna anche il punto di ingresso per la funzione che stiamo per scrivere in csv2sheet :

446e7c7c992c2d8a.png

Ora modifica il corpo della funzione in :

  1. utilizzare le API Cloud Storage e Fogli
  2. contrassegna la funzione csv2sheet come async
  3. recupera fileName dai metadati dell'evento Cloud Storage e ricava 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!
};

È necessario utilizzare async qui per usare await, come vedremo a breve.

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

  • Utilizza il menu a discesa per selezionare l'account di servizio di cui 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 della configurazione, ecco i contenuti package.json con le API Cloud Storage e Fogli Google come due dipendenze che utilizzeremo (usa 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"
    }
}

Dopo aver configurato tutto come descritto, fai clic su "Crea" ! Dopo un breve minuto occorre creare e implementare la funzione.

6. Configura autenticazione e API Fogli

Prima di scrivere altro codice nella funzione Cloud Functions utilizzando l'editor in linea, dobbiamo bloccare la creazione di un'API client di Google con gli ambiti Archiviazione e Foglio appropriati (ricorda che questo fa parte di una funzione async).

Nell'editor delle 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 Fogli :

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

7. Utilizzare l'API Fogli per creare un foglio vuoto

Con un client dell'API Fogli possiamo creare un semplice nuovo foglio nel nostro documento, ma prima di proseguire, ecco una breve nota sul vocabolario:

  • un foglio di lavoro è il documento effettivo a cui fa riferimento il suo identificatore (esaminato sopra e visibile nell'URL del documento)
  • Un foglio è una delle schede del documento a cui può essere fatto riferimento in base al nome (il nome della scheda) o a un identificatore generato al momento della creazione del foglio

Tenendo conto di ciò, ecco una funzione che utilizza il client API Fogli per creare un foglio vuoto nella posizione 2 (in genere dopo il valore predefinito "Sheet1"), con 26 colonne, 2000 righe, con la prima riga bloccata (aggiungila alla tua funzione utilizzando l'editor in linea) :

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);
        }
      }
    );
  });
}

Nota che invece di strutturare come hardcoded il riferimento al foglio di lavoro, ci affidiamo alla variabile di ambiente SPREADSHEET_ID creata in precedenza.

Dobbiamo ricordare il sheetId per ulteriori richieste effettuate a questo foglio specifico. Inoltre, il nome del foglio deve essere univoco e la creazione non riuscirà 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 luogo in cui eseguire il dump dei dati, sviluppiamo ulteriormente la funzione Cloud Functions nell'editor in linea 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

È giunto il momento di completare il foglio che abbiamo creato utilizzando la stessa API client di Fogli e i dati che abbiamo appena raccolto. Cogliamo l'occasione per aggiungere uno stile anche alle colonne del foglio (modificando la dimensione dei caratteri della riga superiore e applicando il 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 dovrebbe essere aggiunto alla funzione Cloud Functions che ora è completa al 99%.

Nota come sia i dati che gli stili vengono combinati come più elementi requests in una singola chiamata batchUpdate dell'API Fogli. Ciò rende l'aggiornamento più efficiente e atomico.

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

Se tutto funziona correttamente, a questo punto puoi:

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

10. Riassumendo e testando il flusso

Le chiamate alle funzioni che abbiamo appena illustrato 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, puoi trovarlo qui (probabilmente più facilmente reperibile in un unico set).

Una volta che hai configurato tutto, carica un file CSV nel bucket corretto e controlla che il foglio di lavoro venga aggiornato con un nuovo foglio con 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. È il momento di smantellare l'infrastruttura

È uno scherzo, non c'è alcuna infrastruttura da demolire, tutto questo è stato fatto in modalità serverless.

Se vuoi, puoi eliminare la funzione Cloud Functions e il bucket che hai creato oppure persino l'intero progetto.

12. Passaggi successivi

Con questo si conclude questo codelab che illustra i 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 alcune operazioni di follow-up :

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

Apprezziamo il tuo feedback.