← back to Rentv Adintel
src/connectors/google-ads.js
173 lines
'use strict';
/**
* Google Ads connector — spec §19.
*
* DISABLED BY DEFAULT. `isEnabled()` returns false unless
* GOOGLE_ADS_ENABLED=true is explicitly set.
*
* Google Ads data MUST remain strictly separate from Search Console data.
* Never mix paid-search metrics with organic GSC metrics in the same table
* or display surface.
*
* No fixtures are provided for Google Ads because enabling it without real
* credentials would produce misleading paid-performance numbers.
*
* When credentials are eventually configured:
* - Set GOOGLE_ADS_ENABLED=true
* - Provide GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CUSTOMER_ID,
* GOOGLE_ADS_LOGIN_CUSTOMER_ID, GOOGLE_ADS_CLIENT_ID,
* GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_REFRESH_TOKEN
* - Implement the TODO real API branch using the google-ads-api npm package
* or direct REST calls to googleads.googleapis.com/v17
*
* @module src/connectors/google-ads
*/
const { query } = require('../../db');
// ---------------------------------------------------------------------------
// Feature gate
// ---------------------------------------------------------------------------
/**
* Returns true only when Google Ads integration is explicitly enabled.
* @returns {boolean}
*/
function isEnabled() {
return process.env.GOOGLE_ADS_ENABLED === 'true';
}
// ---------------------------------------------------------------------------
// Disabled guard — applied to every public function
// ---------------------------------------------------------------------------
function requireEnabled() {
if (!isEnabled()) {
throw new Error(
'Google Ads connector is not configured. ' +
'Set GOOGLE_ADS_ENABLED=true and provide all GOOGLE_ADS_* environment variables ' +
'to enable this integration. Google Ads data is separate from Search Console data.'
);
}
}
// ---------------------------------------------------------------------------
// Connection test
// ---------------------------------------------------------------------------
/**
* Test the Google Ads connection.
* Throws a friendly error when disabled.
*
* @returns {{ connected: boolean, demo: boolean, error?: string }}
*/
async function connectionTest() {
requireEnabled();
// TODO real API:
// const { GoogleAdsClient } = require('google-ads-api'); // not installed
// const client = new GoogleAdsClient({
// developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN,
// client_id: process.env.GOOGLE_ADS_CLIENT_ID,
// client_secret: process.env.GOOGLE_ADS_CLIENT_SECRET,
// refresh_token: process.env.GOOGLE_ADS_REFRESH_TOKEN,
// login_customer_id: process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID,
// });
// const customer = client.Customer({ customer_id: process.env.GOOGLE_ADS_CUSTOMER_ID });
// await customer.query(`SELECT customer.id, customer.descriptive_name FROM customer LIMIT 1`);
throw new Error('Google Ads live API not enabled (install google-ads-api and implement the TODO real API branch)');
}
// ---------------------------------------------------------------------------
// Campaign metrics import
// ---------------------------------------------------------------------------
/**
* Import campaign-level daily metrics into google_ads_campaign_metrics.
*
* TODO real API: GAQL query shape:
* SELECT
* campaign.name,
* segments.date,
* metrics.cost_micros,
* metrics.clicks,
* metrics.impressions,
* metrics.conversions
* FROM campaign
* WHERE segments.date DURING LAST_90_DAYS
* AND campaign.status != 'REMOVED'
* ORDER BY segments.date DESC
*
* @param {{ dryRun?: boolean }} options
*/
async function importCampaignMetrics({ dryRun = false } = {}) {
requireEnabled();
// Implementation pending live credentials
throw new Error('Google Ads not configured');
}
// ---------------------------------------------------------------------------
// Search term metrics import
// ---------------------------------------------------------------------------
/**
* Import search-term level metrics into google_ads_search_term_metrics.
*
* TODO real API: GAQL query shape:
* SELECT
* search_term_view.search_term,
* campaign.name,
* segments.date,
* metrics.clicks,
* metrics.impressions,
* metrics.cost_micros,
* metrics.conversions
* FROM search_term_view
* WHERE segments.date DURING LAST_90_DAYS
* ORDER BY metrics.impressions DESC
* LIMIT 10000
*
* @param {{ dryRun?: boolean }} options
*/
async function importSearchTermMetrics({ dryRun = false } = {}) {
requireEnabled();
throw new Error('Google Ads not configured');
}
// ---------------------------------------------------------------------------
// Full import
// ---------------------------------------------------------------------------
/**
* Import all Google Ads data.
* Only callable when isEnabled() is true.
*
* @param {{ dryRun?: boolean }} options
*/
async function importAll({ dryRun = false } = {}) {
requireEnabled();
throw new Error('Google Ads not configured');
}
// ---------------------------------------------------------------------------
// Connection record
// ---------------------------------------------------------------------------
async function ensureConnectionRecord() {
await query(
`INSERT INTO analytics_connections (kind, status, is_demo)
VALUES ('GOOGLE_ADS', 'NOT_CONNECTED', true)
ON CONFLICT DO NOTHING`
);
}
module.exports = {
isEnabled,
connectionTest,
importCampaignMetrics,
importSearchTermMetrics,
importAll,
ensureConnectionRecord,
};