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