Cloud Function para automatizar la importación de datos CSV a Hojas de cálculo de Google

1. Introducción

El objetivo de este codelab es que comprendas cómo escribir una Cloud Function para reaccionar cuando se sube un archivo CSV a Cloud Storage, leer su contenido y usarlo para actualizar una hoja de cálculo de Google con la API de Hojas de cálculo.

e9c78061022a6760.png

Esto puede verse como la automatización de una "importación como CSV" que, de otro modo, sería manual. paso. Esto garantizará que puedas analizar los datos (tal vez los haya producido otro equipo) en una hoja de cálculo en cuanto estén disponibles.

A continuación, se muestra cómo se ve la implementación :

52df703605ae4bd3.png

2. Configuración y requisitos

Configuración del entorno de autoaprendizaje

  1. Accede a la consola de Cloud y crea un proyecto nuevo o reutiliza uno existente. (Si todavía no tienes una cuenta de Gmail o de G Suite, debes crear una).

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

Recuerde el ID de proyecto, un nombre único en todos los proyectos de Google Cloud (el nombre anterior ya se encuentra en uso y no lo podrá usar). Se mencionará más adelante en este codelab como PROJECT_ID.

  1. A continuación, deberás habilitar la facturación en la consola de Cloud para usar los recursos de Google Cloud recursos.

Ejecutar este codelab no debería costar mucho, tal vez nada. Asegúrate de seguir las instrucciones de la sección “Realiza una limpieza” en la que se aconseja cómo cerrar recursos para no incurrir en facturación más allá de este instructivo. Los usuarios nuevos de Google Cloud son aptos para participar en el programa Prueba gratuita de $300.

3. Crea y configura una hoja de cálculo de Google y habilita la API

Primero, crearemos un nuevo documento de Hojas de cálculo (esta hoja puede pertenecer a cualquier usuario). Una vez creada, recuerda su identificador. se usará como una variable de entorno para la función que escribiremos :

dd77d5fc1364ad3e.png

Desde la consola de Google Cloud, habilita la API de Hojas de cálculo de Google en tu proyecto recién creado. Para ello, navega a “APIs y servicios” y, luego, en "Biblioteca de API" sección :

c64e2e98b8b55f16.png

En la sección “IAM y administrador" ve a "Cuentas de servicio" y anota el correo electrónico de la cuenta de servicio predeterminada de App Engine. Debe tener el formato your-project-id@appspot.gserviceaccount.com. Por supuesto que también puedes crear tu propia cuenta de servicio dedicada a esta acción.

6e279d7e07d4febf.png

Por último, basta con que otorgues a esta cuenta de servicio privilegios de edición para tu hoja de cálculo mediante el botón “Compartir”. botón :

c334062465ddf928.png

Con esta configuración, ahora podemos escribir nuestra Cloud Function y configurarla para usar esta cuenta de servicio. Podrá escribir en este documento de hoja de cálculo que acabamos de crear.

4. Crea un bucket de almacenamiento

Creemos el bucket que nuestra Cloud Function supervisará para detectar nuevos archivos CSV.

En la consola, usa el menú de la izquierda para navegar a “Almacenamiento”. :

2ddcb54423979d25.png

Crea un bucket nuevo llamado csv2sheet-POSTFIX (reemplaza POSTFIX por algo único) con todos los demás parámetros de configuración establecidos en sus valores predeterminados :

dd637080ade62e81.png

5. Crea la Cloud Function

Ahora podemos crear una Cloud Function llamada csv2sheet que se active cuando se suben archivos a un bucket específico de Cloud Storage. El código se escribirá en Node.js 8 con funciones asíncronas mediante el editor directo directamente en la consola de Cloud :

6ee1a5ce63174ae8.png

Asegúrate de configurar el activador en “Cloud Storage” y ajustar el nombre del bucket al que creaste en el paso anterior.

Además, actualiza el punto de entrada de la función que estamos a punto de escribir en csv2sheet :

446e7c7c992c2d8a.png

Ahora, cambia el cuerpo de la función a lo siguiente :

  1. usar las APIs de Cloud Storage y Hojas de cálculo
  2. marca la función csv2sheet como async
  3. Obtén el fileName de los metadatos del evento de Cloud Storage y deriva un nombre para la hoja nueva que crearemos :
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!
};

Se requiere el uso de async aquí para usar await, como veremos en un momento.

Algunas opciones importantes al crear esta función son las siguientes (haz clic en el vínculo "Más" en la parte inferior de la pantalla) :

  • Usa el menú desplegable para seleccionar la cuenta de servicio mencionada anteriormente.
  • Define una variable de entorno llamada SPREADSHEET_ID que debe coincidir con el documento de hoja que creaste anteriormente :

fd22d1873bcb8c66.png

Como paso final de configuración, este es el contenido de package.json con las APIs de Cloud Storage y Hojas de cálculo de Google como las dos dependencias que usaremos (usa la pestaña PACKAGE.JSON del editor directo de la consola) :

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

Cuando hayas configurado todo como se describe, haz clic en "Crear". ! Después de un minuto, tu función debería crearse y, luego, implementarse.

6. Configurar la autenticación y la API de Hojas de cálculo

Antes de escribir más código en nuestra Cloud Function con el editor directo, debemos bloquear la creación de una API de cliente de Google con los permisos de Storage y Hojas de cálculo adecuados (recuerda que esto es parte de una función async).

En el editor de funciones de la consola, haz clic en “EDITAR” y agrega el siguiente código al cuerpo de tu función 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"
  ]
});

Desde allí podemos crear un cliente de la API de Sheets :

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

7. Usa la API de Hojas de cálculo para crear una hoja vacía

Con un cliente de la API de Hojas de cálculo, podemos crear una hoja nueva y sencilla en nuestro documento, pero antes de continuar, aquí tienes una breve nota sobre el vocabulario:

  • Una hoja de cálculo es el documento real y al cual se hace referencia mediante su identificador (que se explica más arriba y se puede ver en la URL del documento)
  • Una hoja es una de las pestañas del documento y se puede hacer referencia a ella por su nombre (el nombre de la pestaña) o un identificador generado cuando se crea la hoja.

Teniendo esto en cuenta, esta es una función que usa el cliente de la API de Hojas de cálculo para crear una hoja vacía en la posición 2 (generalmente después de la “Hoja1”) predeterminada con 26 columnas, 2, 000 filas, con la primera fila inmovilizada (agrégala a tu función con el editor directo) :

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

Ten en cuenta que, en lugar de codificar la referencia a la hoja de cálculo, nos basamos en la variable de entorno SPREADSHEET_ID creada anteriormente.

Debemos recordar el sheetId para las solicitudes adicionales que se realicen a esta hoja en particular. Además, el nombre de la hoja debe ser único, y la creación fallará si ya existe una hoja llamada sheetName.

La función batchUpdate de la API de Hojas de cálculo es una forma común de interactuar con documentos y se describe aquí.

8. Lee datos de un archivo CSV de almacenamiento

Ahora que tenemos un lugar para volcar los datos, desarrollemos aún más la Cloud Function en el editor directo y usemos la API de Cloud Storage para tomar los datos reales del archivo que se acaba de subir y almacenarlos en una cadena:

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. Propagar la hoja de cálculo recién creada

Es el momento de completar la hoja de cálculo que creamos con la misma API de cliente de Hojas de cálculo y los datos que acabamos de recopilar. Aprovecharemos esta oportunidad para agregar también un poco de estilo a las columnas de la hoja (cambiar el tamaño de la fuente de la fila superior y ponerla en negrita) :

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

Deberías agregar este código a nuestra Cloud Function, que ahora está 99% completa.

Observa cómo los datos y el estilo se combinan como varios requests en una sola llamada batchUpdate a la API de Hojas de cálculo. Esto hace que la actualización sea más eficiente y atómica.

Ten en cuenta también que definimos un rango de edición que coincide con el tamaño de la hoja que hemos creado. Esto significa que el contenido que supere las 26 columnas (el valor columnCount que se usó al crear la hoja) fallará con este código específico.

Si todo va bien, en este punto puedes hacer lo siguiente:

  1. guarda la función actualizada
  2. soltará un archivo CSV en el bucket
  3. verás cómo aparecen los datos correspondientes en tu hoja de cálculo.

10. Revisión general y prueba del flujo

Las llamadas a las funciones que acabamos de analizar se pueden realizar como llamadas de bloqueo sucesivas en la función csv2sheet original:

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

Si necesitas el código fuente completo de la función, está disponible aquí (probablemente más fácil de obtener todo en un conjunto).

Una vez que todo esté listo, sube un archivo CSV al bucket correcto y observa cómo tu hoja de cálculo se actualiza con una nueva hoja con el contenido del archivo. Si no tienes uno a mano, consulta este archivo CSV de muestra.

1efae021942e64fa.png

Intenta subir varios archivos al bucket para ver qué sucede.

11. Eso es todo. Es hora de destruir la infraestructura

Es broma, no hay infraestructura para eliminar, todo esto se hizo sin servidores.

Si lo deseas, puedes borrar la Cloud Function y el bucket que creaste, o incluso todo el proyecto.

12. Próximos pasos

De esta manera, concluye este codelab, que te guiará por los pasos para escuchar cargas a un bucket de Cloud Storage en una Cloud Function para actualizar una hoja de cálculo de Google con la API adecuada.

Aquí tiene algunos pasos para continuar:

Si tienes problemas con este codelab, infórmalos mediante el vínculo que se encuentra en la esquina inferior izquierda.

Agradecemos tus comentarios.