Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
/**
* ABOUT THIS CODE
* This file, along with a manifest appsscript.json comprises a
* Google Data Studio Community Connector.
* Maintained aaron@impression.co.uk | www.impression.co.uk
*/
/**
* This is one of the four requisites of a Data Studio connector.
* This function sends the Data Studio configuration parameters upon request.
* Note that this example does not use a date range selector - where
* dateRangeRequired would be set to true
* See: https://developers.google.com/datastudio/connector/build
*
* @param {string} request - The configuration request from Data Studio.
* @returns {object}
*/
function getConfig(request) {
var config = {
"configParams": [
{
"type": "INFO",
"name": "welcomeMessage",
"text": "Full instructions tbc"
},
{
"type": "TEXTINPUT",
"name": "apiKey",
"displayName": "STAT API Key",
"helpText": "You'll be provided with this :)",
"placeholder": "STAT API Key"
},
{
"type": "TEXTINPUT",
"name": "siteId",
"displayName": "STAT Site ID",
"helpText": "We need to retrieve this through the API",
"placeholder": "STAT Site ID"
},
{
"type": "TEXTINPUT",
"name": "siteName",
"displayName": "STAT Site subdomain",
"helpText": "The subdomain of your stat login url.",
"placeholder": "STAT Site subdomain"
}
]
};
return config;
}
/**
* This is one of the four requisites of a Data Studio connector.
* Store this in the global scope for reference across the Script.
* See: https://developers.google.com/datastudio/connector/build
*/
var dataSchema = [
{
name: 'keyword',
label: 'Keyword',
dataType: 'STRING',
semantics: {
conceptType: 'DIMENSION'
}
},
{
name: 'regionalSearchVolume',
label: 'Regional Search Volume',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC',
isReaggregatable: true
}
},
{
name: 'googleRank',
label: 'Google Rank',
dataType: 'NUMBER',
semantics: {
conceptType: 'METRIC',
isReaggregatable: false
}
}
];
/**
* This is one of the four requisites of a Data Studio connector.
* This function sends the Data Studio schema upon request.
* See: https://developers.google.com/datastudio/connector/build
*
* @param {string} request - The request from Data Studio.
* @returns {object}
*/
function getSchema(request) {
return {schema: dataSchema};
}
/**
* This is one of the four requisites of a Data Studio connector.
* This function specifies the required authentication for this Script.
* In this example, no OAuth authentication is required
* as the API key is a configParam
* For OAuth see: https://developers.google.com/datastudio/connector/oauth2
*
* @returns {object}
*/
function getAuthType() {
var response = {
"type": "NONE"
};
return response;
}
/**
* This is one of the four requisites of a Data Studio connector.
* This function is the main one called once a table or chart has been placed on
* to the canvas. The request object includes all configParams with values,
* as well as a fields array of the selected dimensions and metrics (in order)
* so that that the appropriate headers and rows can be returned to satisfy
* the request.
*
* See: https://developers.google.com/datastudio/connector/build
*
* @param {string} request - The request from Data Studio.
* @returns {object}
*/
function getData(request) {
var header_rows = [];
for (var i = 0; i < request.fields.length; i++) {
for (var j = 0; j < dataSchema.length; j++) {
if (dataSchema[j].name == request.fields[i].name) {
header_rows.push(dataSchema[i]);
}
}
}
var url_parts = [
'https://',
request.configParams.siteName,
'.getstat.com/api/v2/',
request.configParams.apiKey,
'/keywords/list?site_id=',
request.configParams.siteId,
'&format=json'
];
var all_results = get_keywords_recursive(request.configParams);
if (all_results.length > 0) {
var rankings_rows = [];
for (var i = 0; i < all_results.length; i++) {
var keyword_obj = all_results[i];
var rankings_row = [];
for (var j = 0; j < header_rows.length; j++) {
switch (header_rows[j].name) {
case 'keyword':
rankings_row.push(keyword_obj.Keyword);
break;
case 'regionalSearchVolume':
rankings_row.push(keyword_obj.KeywordStats.RegionalSearchVolume);
break;
case 'googleRank':
rankings_row.push(keyword_obj.KeywordRanking.Google.Rank || 101);
break;
}
}
rankings_rows.push({
'values': rankings_row
});
}
var return_data = {
schema: header_rows,
rows: rankings_rows,
cachedData: false
};
return return_data;
} else {
console.info('The STAT API returned a non-200 response. Please check your settings and try again');
throw ("The STAT API returned a non-200 response. Please check your settings and try again. ");
}
}
/**
* Keywords are called from the STAT API one page at a time. This recursively allows for
* pagination of these results into sequential requests to the API endpoint.
* Each request results are then appended to the existing results and then eventually returned
* back to the getData() function.
*
* @param {object} settings - This is the configParams from the Data Studio request.
* @param {array} existing_results - The request from Data Studio.
* @param {string} request - The request from Data Studio.
* @returns {array}
*/
function get_keywords_recursive( settings, existing_results, next_url ) {
existing_results = existing_results || [];
next_url = next_url || false;
var url_parts = ['https://', settings.siteName, '.getstat.com/api/v2/', settings.apiKey];
if (existing_results.length == 0) {
url_parts.push('/keywords/list?site_id=');
url_parts.push(settings.siteId);
url_parts.push('&format=json');
} else {
url_parts.push(next_url);
}
var request_url = url_parts.join('');
var response = UrlFetchApp.fetch( request_url );
var results = JSON.parse( response.getContentText() );
if (results.Response.nextpage) {
existing_results = existing_results.concat(results.Response.Result);
return existing_results.concat( get_keywords_recursive( settings, existing_results, results.Response.nextpage ) );
} else {
return results.Response.Result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment