Para enviar os dados dos eventos para o Big Query utilizando Cloud Functions é necessário realizar os passos a seguir:
- Criação de dataset e tabela no Big Query;
- Criação de Cloud Function;
- Adequação do custom template para envio de requisições para a Cloud Function.
Para criar a tabela acesse o GCP (Google Cloud Plataform) e crie um dataset com o nome dp6_media_quality
e uma tabela com o nome media-quality-raw
.
As colunas criadas na tabela são:
Nome da Coluna | Descrição |
---|---|
client_id | Client id do Google Analytics |
media_name | Nome da mídia que foi disparada |
tracking_id | Id de acompanhamento da mídia disparada |
media_event | Nome do evento disparado |
tag_name | Nome completo da tag disparada no GTM |
status | Status de disparo da tag |
datalayer_event | Nome do evento do DataLayer que acionou a tag |
timestamp | Data e hora do registro |
Ao criar a tabela selecione a opção para realizar o particionamento diário dos dados utilizando a coluna timestamp
. O código abaixo contém um JSON com o esquema da tabela criada.
// Esquema da tabela criada no Big Query
[
{
name: 'client_id',
type: 'STRING',
mode: 'NULLABLE',
description: 'Client id do Google Analytics',
maxLength: '100',
},
{
name: 'media_name',
type: 'STRING',
mode: 'NULLABLE',
description: 'Nome da midia que foi disparada',
maxLength: '100',
},
{
name: 'tracking_id',
type: 'STRING',
mode: 'NULLABLE',
description: 'Id de acompanhamento da midia disparada',
maxLength: '100',
},
{
name: 'media_event',
type: 'STRING',
mode: 'NULLABLE',
description: 'Nome do evento disparado',
maxLength: '100',
},
{
name: 'tag_id',
type: 'STRING',
mode: 'NULLABLE',
description: 'ID da tag disparada no GTM',
maxLength: '100',
,{
name: 'tag_name',
type: 'STRING',
mode: 'NULLABLE',
description: 'Nome completo da tag disparada no GTM',
maxLength: '100',
},
{
name: 'status',
type: 'STRING',
mode: 'NULLABLE',
description: 'Status de disparo da tag',
maxLength: '50',
},
{
name: 'datalayer_event',
type: 'STRING',
mode: 'NULLABLE',
description: 'Nome do evento do DataLayer que acionou a tag',
maxLength: '100',
},
{
name: 'timestamp',
type: 'TIMESTAMP',
mode: 'REQUIRED',
description: 'Data e hora do registro',
},
];
Para criar a Cloud Function acesse o GCP (Google Cloud Plataform) e utilize código diponibilizado abaixo (index.js e package.json). Foram utilizados Runtime: Node.js 16
e Entry point: gtm_monitor
. É importante verificar se a Cloud Function está acessível, portanto, verifique a secção Permissions
para habilitar as permissões necessárias. Para a criação da function foram usados os arquivos index.js
e package.json
.
A function recebe uma requisição HTTP que pode conter dados em JSON ou uma URL com query params. Para selecionar uma das opções é preciso alterar o valor da constante input_option
localizada nas primeiras linhas de código (no arquivo index.js).
Os dados em formato JSON recebidos pela function estão no seguinte formato:
{
"client_id": "1101944939.1645464696"
"media_name": "media_name",
"tracking_id": "123",
"media_event": "media_event",
"tag_id": "3"
"tag_name": "tag_name",
"status": "status",
"datalayer_event": "datalayer_event",
"timestamp": 1652359111.576
}
Caso os dados recebidos pelo Cloud Function seja uma URL ela será do seguinte formato:
https://{{URL da Cloud Function}}/?client_id={{client_id}}&media_name={{media_name}}&tracking_id={{tracking_id}}&media_event={{media_event}} ...
As informações provenientes da URL são organizadas em um dicionário após a extração por meio de expressões regulares. Posteriormente os dados são enviados para o Big Query.
index.js
// Import the Google Cloud client library
const { BigQuery } = require('@google-cloud/bigquery');
const bigquery = new BigQuery();
// Request origin allowed in cloud function
var request_origin = process.env.REQUEST_ORIGIN;
request_origin = request_origin.split(',');
// Select what kind of data req.body contains. If the data
// comes from sendPixel method (used on GTM custom template) use "url" else use "json"
const input_option = 'json'; // url ou json
async function insertRowsAsStream(request, input_option) {
const datasetId = 'dp6_media_quality';
const tableId = 'media-quality-raw';
var json_data;
var json_data_raw;
if (input_option == 'url') {
const url = decodeURI(request.protocol + '://' + request.get('host') + request.originalUrl);
json_data = {
client_id: url.match('client_id=([^&]+)')[1],
media_name: url.match('media_name=([^&]+)')[1],
tracking_id: url.match('tracking_id=([^&]+)')[1],
media_event: url.match('media_event=([^&]+)')[1],
tag_id: url.match('tag_id=([^&]+)')[1],
tag_name: url.match('tag_name=([^&]+)')[1],
status: url.match('status=([^&]+)')[1],
datalayer_event: url.match('datalayer_event=([^&]+)')[1],
timestamp: Date.now() / 1000,
page: url.match('page=([^&]+)')[1],
container_version: url.match('container_version=([^&]+)')[1],
};
}
if (input_option == 'json') {
try {
// Parse a JSON
json_data_raw = JSON.parse(request.body);
} catch (e) {
json_data_raw = request.body;
}
json_data_raw['timestamp'] = Date.now() / 1000;
lst_allowed_fields = [
'client_id',
'media_name',
'tracking_id',
'media_event',
'tag_id',
'tag_name',
'status',
'datalayer_event',
'timestamp',
'page',
'container_version',
];
json_data = Object.fromEntries(Object.entries(json_data_raw).filter(([key]) => lst_allowed_fields.includes(key)));
}
// Insert data into a table
await bigquery.dataset(datasetId).table(tableId).insert(json_data);
}
exports.gtm_monitor = (req, res) => {
if (req.body && request_origin.includes(req.headers.origin)) {
insertRowsAsStream(req, input_option);
res.sendStatus(200);
} else {
console.log('Requisição inválida. Verifique o payload ou a variável REQUEST_ORIGIN...');
res.sendStatus(403);
}
};
package.json
{
"name": "dp6-cf-media-quality",
"version": "1.0.0",
"description": "envia dados para o bigquery atraves de cloud function",
"author": "dp6",
"dependencies": {
"@google-cloud/bigquery": "^2.1.0"
},
"license": "ISC"
}
Existem duas maneiras de enviar os dados para a Cloud Function, uma utilizando o método sendPixel
e a outra utizando Fetch
.
O sendPixel é utilizado para realizar requisições do tipo GET. Ela recebe como parâmetro uma URL que é composta por URL = endpoint + query params
. O endpoint é a URL da Cloud Function enquanto que os query params contém os dados de mídia que serão enviados para a Cloud Function.
O fetch
permite realizar requisições do tipo POST e o envio de dados no formato JSON. No GTM deve-se criar uma variável do tipo custom javascript e inserir a função responsável pela requisição. Na tag do GTM o campo sendFetchReference
deve ser preenchido com a variável criada.
Código javascript utilizado no template de Media Quality (GTM)
...
const encodeUri = require('encodeUri');
const sendPixel = require('sendPixel');
const sendRequestFetch = data.sendFetchReference;
...
function sendToCF(method) {
const endpoint = data.cfEndpoint;
const event = readFromDataLayer('event');
const fetch = data.fetchReference;
addEventCallback(function(containerId, eventData) {
const tagData = eventData.tags.filter(t => t.exclude === 'false');
for (let i in tagData) {
let entry = tagData[i];
let body = {
client_id: data.clientId,
media_name: data.autoCollect ? entry.media_name : entry.name.split(' - ')[0].split(' (')[0],
tracking_id: entry.tracking_id,
media_event: data.autoCollect ? entry.media_event : entry.name.split(' - ')[1],
tag_name: entry.name,
status: entry.status,
datalayer_event: event
};
for (let j in data.params) {
let name = data.params[j].param;
let value = data.params[j].value;
body[name] = value;
}
//Send data via GET method
if (method == 'get') {
var url = "";
for (let item in body) {
url += '&' + item + '=' + body[item];
}
url = endpoint+ "/?" + encodeUri(url);
sendPixel(url,null,null);
}
//Send data via POST method
else if (method == 'post') {
fetch(endpoint, body);
}
}
});
}
...
Função Fetch (utilizada na custom javascript variable do GTM)
function(){
function CustomFetch(endpoint, payload){
fetch(endpoint, {
method: "POST",
mode: 'no-cors',
body: JSON.stringify(payload),
headers: {'Content-Type': 'application/json'}
});
}
return CustomFetch;
}
Para criar a Cloud function acesse o console do Google Cloud e clique em Create Function
(Figura 1).
Na etapa de configuração selecione Allow unauthenticated invocations
e marque Require HTTPS
Crie uma variável de ambiente com o nome REQUEST_ORIGIN
e adicione as URLs das página web separadas por vígula (ex.: https://dp6.com.br,https://dp6.github.io). A Cloud Function apenas será disparada se a requisição for proveniente dos sites listados na variável REQUEST_ORIGIN
.
Na aba de permissões, allUsers
deve possuir o papel Cloud Functions Invoker
Criação de variável javascript com o código responsável pelas requisições HTTP. Caso seja utilizada a outra forma de envio de dados (sendPixel) não é necessário criar essa variável.
Após habilitar na tag o "Endpoint de destino" como Cloud Function deve-se inserir a URL de trigger da Cloud Function, a variável javascript criada anteriormente e um segredo (é o secret da cloud function) a ser adicionado na requisição HTTP, conforme o exemplo abaixo (Figura 2).