Reputation: 13
I am trying to create a looker studio Google ads connector and I am currently stuck.
Anytime I try to click on the "Authorize" button (please see attached) to initiate connection, it does not do anything. The button will just flicker and no action will be taken and no request will be initiated. Please help. "Authorize" button not working
/***************************************
* 0) ADMIN USER CHECK
***************************************/
function isAdminUser() {
return true;
}
/***************************************
* 1) CONSTANTS & OAUTH SETUP
***************************************/
var CLIENT_ID = '';
var CLIENT_SECRET = '';
var DEVELOPER_TOKEN = '';
/**
* Returns the OAuth2 service
* @return {OAuth2Service}
*/
function getOAuthService_() {
return OAuth2.createService('googleAds')
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://oauth2.googleapis.com/token')
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
.setLock(LockService.getUserLock())
.setScope('https://www.googleapis.com/auth/adwords')
.setParam('access_type', 'offline')
.setParam('prompt', 'consent')
.setTokenFormat(OAuth2.TOKEN_FORMAT.JSON);
}
/**
* Required for Looker Studio - Authentication Type
*/
function getAuthType() {
return {
type: 'OAUTH2',
helpUrl: 'https://developers.google.com/google-ads/api/docs/oauth/overview'
};
}
/**
* Required for third-party authorization in Looker Studio
* @return {Object} The authorization information.
*/
function get3PAuthorizationUrls() {
var service = getOAuthService_();
return {
type: 'OAUTH2',
authorizationUrl: service.getAuthorizationUrl(),
tokenUrl: 'https://oauth2.googleapis.com/token',
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET
};
}
/**
* Handles the OAuth2 callback
*/
function authCallback(request) {
var authorized = getOAuthService_().handleCallback(request);
if (authorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Access denied. Please try again.');
}
}
/**
* Checks if the user is authorized
*/
function isAuthValid() {
return getOAuthService_().hasAccess();
}
/**
* Resets the OAuth2 service
*/
function resetAuth() {
getOAuthService_().reset();
}
/***************************************
* 2) CONFIG: User Account Selection
***************************************/
function getConfig(request) {
var service = getOAuthService_();
if (!service.hasAccess()) {
return {
errorCode: "OAUTH_LOGIN_REQUIRED",
isAuthValid: false
};
}
var cc = DataStudioApp.createCommunityConnector();
var config = cc.getConfig();
try {
var customerIds = fetchAccessibleCustomers_();
if (customerIds && customerIds.length > 0) {
config.newSelectSingle()
.setId('selectedCustomerId')
.setName('Google Ads Account')
.setHelpText('Select your Google Ads Account')
.addOptions(config.newOptionBuilder()
.setOptions(customerIds.map(function(resourceName) {
var cid = resourceName.split('/')[1];
return {
label: cid,
value: cid
};
}))
);
} else {
config.newInfo()
.setId('noAccountsInfo')
.setText('No accessible Google Ads accounts found.');
}
config.setDateRangeRequired(true);
return config.build();
} catch (e) {
cc.newUserError()
.setDebugText('Error in getConfig: ' + e.message)
.setText('Error fetching Google Ads accounts. Please try again later.')
.throwException();
}
}
/***************************************
* 3) SCHEMA: Field Definitions
***************************************/
function getSchema(request) {
var cc = DataStudioApp.createCommunityConnector();
var fields = cc.getFields();
var types = cc.FieldType;
fields.newDimension()
.setId('campaign_id')
.setName('Campaign ID')
.setType(types.TEXT);
fields.newDimension()
.setId('campaign_name')
.setName('Campaign Name')
.setType(types.TEXT);
fields.newMetric()
.setId('impressions')
.setName('Impressions')
.setType(types.NUMBER);
fields.newMetric()
.setId('clicks')
.setName('Clicks')
.setType(types.NUMBER);
fields.newMetric()
.setId('cost')
.setName('Cost')
.setType(types.CURRENCY_USD);
return fields.build();
}
/***************************************
* 4) DATA: Fetch and Transform
***************************************/
function getData(request) {
var service = getOAuthService_();
if (!service.hasAccess()) {
return {
errorCode: "OAUTH_LOGIN_REQUIRED",
isAuthValid: false
};
}
var customerIds = request.configParams.selectedCustomerId;
try {
var query = buildQuery_(request);
var data = fetchGoogleAdsData_(customerIds, query);
return {
schema: request.fields,
rows: transformData_(data, request.fields)
};
} catch (e) {
DataStudioApp.createCommunityConnector()
.newUserError()
.setDebugText('Error fetching data: ' + e.message)
.setText('Error retrieving data from Google Ads.')
.throwException();
}
}
/***************************************
* Helper Functions
***************************************/
function buildQuery_(request) {
var fields = request.fields.map(function(field) {
switch(field.getId()) {
case 'campaign_id': return 'campaign.id';
case 'campaign_name': return 'campaign.name';
case 'impressions': return 'metrics.impressions';
case 'clicks': return 'metrics.clicks';
case 'cost': return 'metrics.cost_micros';
default: return field.getId();
}
});
return [
'SELECT ',
fields.join(', '),
' FROM campaign',
' WHERE segments.date BETWEEN "',
request.dateRange.startDate,
'" AND "',
request.dateRange.endDate,
'"'
].join('');
}
function fetchAccessibleCustomers_() {
var service = getOAuthService_();
if (!service.hasAccess()) {
throw new Error('Authorization required.');
}
var accessToken = service.getAccessToken();
var url = 'https://googleads.googleapis.com/v13/customers:listAccessibleCustomers';
var options = {
method: 'get',
headers: {
'Authorization': 'Bearer ' + accessToken,
'developer-token': DEVELOPER_TOKEN
},
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, options);
var data = JSON.parse(response.getContentText());
if (data.error) {
throw new Error('Google Ads API Error: ' + JSON.stringify(data.error));
}
return data.resourceNames || [];
}
function fetchGoogleAdsData_(customerId, query) {
var service = getOAuthService_();
if (!service.hasAccess()) {
throw new Error('Authorization required.');
}
var accessToken = service.getAccessToken();
var url = 'https://googleads.googleapis.com/v13/customers/' + customerId + '/googleAds:search';
var options = {
method: 'post',
headers: {
'Authorization': 'Bearer ' + accessToken,
'developer-token': DEVELOPER_TOKEN,
'Content-Type': 'application/json'
},
payload: JSON.stringify({ query: query }),
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, options);
var data = JSON.parse(response.getContentText());
if (data.error) {
throw new Error('Google Ads API Error: ' + JSON.stringify(data.error));
}
return data;
}
function transformData_(apiResponse, requestedFields) {
if (!apiResponse || !apiResponse.results) return [];
return apiResponse.results.map(function(row) {
var values = requestedFields.map(function(field) {
var fieldId = field.getId();
switch(fieldId) {
case 'campaign_id': return row.campaign.id;
case 'campaign_name': return row.campaign.name;
case 'impressions': return row.metrics.impressions;
case 'clicks': return row.metrics.clicks;
case 'cost': return row.metrics.cost_micros / 1000000;
default: return '';
}
});
return { values: values };
});
}
Upvotes: 1
Views: 57