> ## Documentation Index
> Fetch the complete documentation index at: https://docs.analytics.synapside.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Exemplo Prático: Google Ads -> CDP

Neste tutorial completo, você aprenderá a criar uma função de dados no **Analytics Lab** que extrai dados de campanhas e métricas diárias da API do **Google Ads** e realiza a ingestão em massa (batch insert) no **CDP Synapside**.

***

## 🏗️ 1. Criar as Tabelas no CDP

Antes de executar a função, criamos duas tabelas no CDP para receber os dados:

1. `google_ads_campaigns`: Metadados e status de cada campanha.
2. `google_ads_metrics`: Histórico diário de cliques, impressões, custos e conversões.

### Opção A: Via API REST do CDP (`POST /api/cdp/schema/table`)

```bash theme={null}
# 1. Tabela de Campanhas
curl -X POST "https://seu-cdp-host/api/cdp/schema/table" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SEU_CDP_TOKEN" \
  -d '{
    "table_name": "google_ads_campaigns",
    "columns": [
      { "name": "id", "type": "TEXT", "comment": "ID único da campanha no Google Ads" },
      { "name": "name", "type": "TEXT", "comment": "Nome da campanha" },
      { "name": "status", "type": "TEXT", "comment": "Status da campanha" },
      { "name": "date", "type": "TEXT", "comment": "Data da sincronização" }
    ],
    "comment": "Tabela de campanhas do Google Ads"
  }'

# 2. Tabela de Métricas
curl -X POST "https://seu-cdp-host/api/cdp/schema/table" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SEU_CDP_TOKEN" \
  -d '{
    "table_name": "google_ads_metrics",
    "columns": [
      { "name": "id", "type": "TEXT", "comment": "ID composto único (campaign_id_date)" },
      { "name": "campaign_id", "type": "TEXT", "comment": "ID da campanha" },
      { "name": "date", "type": "TEXT", "comment": "Data da métrica (YYYY-MM-DD)" },
      { "name": "impressions", "type": "INTEGER", "comment": "Total de impressões" },
      { "name": "clicks", "type": "INTEGER", "comment": "Total de cliques" },
      { "name": "cost_micros", "type": "FLOAT", "comment": "Custo em micros" },
      { "name": "conversions", "type": "FLOAT", "comment": "Total de conversões" },
      { "name": "conversions_value", "type": "FLOAT", "comment": "Valor total de conversões" }
    ],
    "comment": "Métricas diárias de performance do Google Ads"
  }'
```

***

### Opção B: Via SQL Direto no CDP (`POST /api/cdp/sql`)

```sql theme={null}
CREATE TABLE IF NOT EXISTS google_ads_campaigns (
  id TEXT PRIMARY KEY,
  name TEXT,
  status TEXT,
  date TEXT
);

CREATE TABLE IF NOT EXISTS google_ads_metrics (
  id TEXT PRIMARY KEY,
  campaign_id TEXT,
  date TEXT,
  impressions INTEGER,
  clicks INTEGER,
  cost_micros FLOAT,
  conversions FLOAT,
  conversions_value FLOAT
);
```

***

## 📦 2. Estrutura dos Arquivos da Função

### `package.json`

```json theme={null}
{
  "name": "google-ads-ingestion",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "google-ads-api": "^24.1.0"
  }
}
```

### `index.js`

```javascript theme={null}
import { GoogleAdsApi } from 'google-ads-api';

function getAdsClient(credentials) {
  return new GoogleAdsApi({
    client_id: credentials.clientId,
    client_secret: credentials.clientSecret,
    developer_token: credentials.developerToken,
  });
}

function getCustomer(client, credentials) {
  const customerId = String(credentials.customerId || '').replace(/-/g, '');
  const loginCustomerId = String(credentials.loginCustomerId || '').replace(/-/g, '');

  const options = {
    customer_id: customerId,
    refresh_token: credentials.refreshToken,
  };

  if (loginCustomerId) {
    options.login_customer_id = loginCustomerId;
  }

  return client.Customer(options);
}

function buildGaqlQuery(daysAgo = 2) {
  const today = new Date();
  const startDate = new Date();
  startDate.setDate(today.getDate() - daysAgo);

  const formatDate = (date) => {
    const y = date.getFullYear();
    const m = String(date.getMonth() + 1).padStart(2, '0');
    const d = String(date.getDate()).padStart(2, '0');
    return `${y}-${m}-${d}`;
  };

  const startStr = formatDate(startDate);
  const endStr = formatDate(today);

  return `
    SELECT
      segments.date,
      campaign.id,
      campaign.name,
      campaign.status,
      metrics.impressions,
      metrics.clicks,
      metrics.cost_micros,
      metrics.conversions,
      metrics.conversions_value
    FROM campaign
    WHERE segments.date BETWEEN '${startStr}' AND '${endStr}'
      AND campaign.status != 'REMOVED'
    ORDER BY segments.date DESC
  `;
}

function transformResults(rows) {
  const campaignsMap = new Map();
  const metrics = [];

  for (const row of rows) {
    const campaignId = String(row.campaign?.id || '');
    const date = String(row.segments?.date || '');

    if (campaignId && !campaignsMap.has(campaignId)) {
      campaignsMap.set(campaignId, {
        id: campaignId,
        name: String(row.campaign?.name || ''),
        status: String(row.campaign?.status || ''),
        date: date,
      });
    }

    metrics.push({
      id: `${campaignId}_${date}`,
      campaign_id: campaignId,
      date: date,
      impressions: Number(row.metrics?.impressions || 0),
      clicks: Number(row.metrics?.clicks || 0),
      cost_micros: Number(row.metrics?.cost_micros || 0),
      conversions: Number(row.metrics?.conversions || 0),
      conversions_value: Number(row.metrics?.conversions_value || 0),
    });
  }

  return {
    campaigns: Array.from(campaignsMap.values()),
    metrics: metrics,
  };
}

async function sendBatch(cdpUrl, token, table, data) {
  if (!data || data.length === 0) {
    return { table, count: 0, status: 'empty' };
  }

  const response = await fetch(`${cdpUrl}/api/cdp/data/${table}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    },
    body: JSON.stringify(data),
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(`Erro ao enviar para ${table}: ${JSON.stringify(body)}`);
  }

  return { table, count: data.length, result: body };
}

export async function handler(event, context) {
  const credentials = {
    developerToken: event?.developer_token || process.env.GADS_DEVELOPER_TOKEN,
    clientId: event?.client_id || process.env.GADS_CLIENT_ID,
    clientSecret: event?.client_secret || process.env.GADS_CLIENT_SECRET,
    refreshToken: event?.refresh_token || process.env.GADS_REFRESH_TOKEN,
    customerId: event?.customer_id || process.env.GADS_CUSTOMER_ID,
    loginCustomerId: event?.login_customer_id || process.env.GADS_LOGIN_CUSTOMER_ID,
  };

  const cdpConfig = {
    url: event?.cdp_url || process.env.CDP_API_URL || 'http://localhost:3001',
    token: event?.cdp_token || process.env.CDP_API_TOKEN || 'dev_token_123456',
    campaignsTable: event?.campaigns_table || 'google_ads_campaigns',
    metricsTable: event?.metrics_table || 'google_ads_metrics',
  };

  const daysAgo = event?.days_ago ?? 2;

  const adsClient = getAdsClient(credentials);
  const customer = getCustomer(adsClient, credentials);

  const query = buildGaqlQuery(daysAgo);
  const rows = await customer.query(query);

  const { campaigns, metrics } = transformResults(rows);

  const campaignsResult = await sendBatch(
    cdpConfig.url,
    cdpConfig.token,
    cdpConfig.campaignsTable,
    campaigns
  );

  const metricsResult = await sendBatch(
    cdpConfig.url,
    cdpConfig.token,
    cdpConfig.metricsTable,
    metrics
  );

  return {
    status: 'success',
    total_rows_received: rows.length,
    ingestion: {
      campaigns: campaignsResult,
      metrics: metricsResult,
    },
  };
}
```

***

## 🚀 3. Criando, Testando e Publicando via CLI

### 1. Criar a Função no Lab

```bash theme={null}
sanalytics create \
  -name "Google Ads Ingestion" \
  -slug "google-ads-ingestion" \
  -runtime "node22" \
  -lang "js" \
  -dir "google-ads-ingestion" \
  -entrypoint "index.js:handler"
```

### 2. Testar no Sandbox

```bash theme={null}
cd google-ads-ingestion
sanalytics test
```

### 3. Fazer Deploy para Produção

```bash theme={null}
sanalytics deploy
```
