highcharts
Version:
JavaScript charting framework
212 lines (211 loc) • 5.69 kB
JavaScript
/* *
*
* (c) 2009-2026 Highsoft AS
*
* A commercial license may be required depending on use.
* See www.highcharts.com/license
*
*
* Authors:
* - Torstein Hønsi
* - Gøran Slettemark
* - Wojciech Chmiel
* - Sophie Bremer
* - Jomar Hønsi
* - Kamil Kubik
*
* */
;
import DataConnector from './DataConnector.js';
import GoogleSheetsConverter from '../Converters/GoogleSheetsConverter.js';
import { fireEvent, merge, pick } from '../../Shared/Utilities.js';
/* *
*
* Functions
*
* */
/**
* Tests Google's response for error.
* @private
*/
function isGoogleError(json) {
return (typeof json === 'object' && json &&
typeof json.error === 'object' && json.error &&
typeof json.error.code === 'number' &&
typeof json.error.message === 'string' &&
typeof json.error.status === 'string');
}
/* *
*
* Class
*
* */
/**
* @private
* @todo implement save, requires oauth2
*/
class GoogleSheetsConnector extends DataConnector {
/* *
*
* Constructor
*
* */
/**
* Constructs an instance of GoogleSheetsConnector
*
* @param {Partial<GoogleSheetsConnectorOptions>} [options]
* Options for the connector and converter.
*/
constructor(options) {
const mergedOptions = merge(GoogleSheetsConnector.defaultOptions, options);
super(mergedOptions);
this.options = mergedOptions;
}
/* *
*
* Functions
*
* */
/**
* Overrides the DataConnector method. Emits an event on the connector to
* all registered callbacks of this event.
*
* @param {Event} e
* Event object containing additional event information.
*/
emit(e) {
fireEvent(this, e.type, e);
}
/**
* Loads data from a Google Spreadsheet.
*
* @param {DataEventDetail} [eventDetail]
* Custom information for pending events.
*
* @return {Promise<this>}
* Same connector instance with modified table.
*/
load(eventDetail) {
const connector = this;
const options = connector.options;
const { dataRefreshRate, enablePolling, googleAPIKey, googleSpreadsheetKey, dataTables } = options;
const url = buildFetchURL(googleAPIKey, googleSpreadsheetKey, options);
connector.emit({
type: 'load',
detail: eventDetail,
url
});
if (!URL.canParse(url)) {
throw new Error('Invalid URL: ' + url);
}
return fetch(url, { signal: connector?.pollingController?.signal })
.then((response) => (response.json()))
.then((json) => {
if (isGoogleError(json)) {
throw new Error(json.error.message);
}
this.initConverters(json, (key) => {
const tableOptions = dataTables?.find((dataTable) => dataTable.key === key);
// The data table options takes precedence over the
// connector options.
const { firstRowAsNames = options.firstRowAsNames, beforeParse = options.beforeParse } = tableOptions || {};
const converterOptions = {
firstRowAsNames,
beforeParse
};
return new GoogleSheetsConverter(converterOptions);
}, (converter, data) => converter.parse({ json: data }));
return connector.applyTableModifiers();
})
.then(() => {
connector.emit({
type: 'afterLoad',
detail: eventDetail,
url
});
// Polling
if (enablePolling) {
setTimeout(() => connector.load(), Math.max(dataRefreshRate || 0, 1) * 1000);
}
return connector;
})['catch']((error) => {
connector.emit({
type: 'loadError',
detail: eventDetail,
error
});
throw error;
});
}
}
/* *
*
* Static Properties
*
* */
GoogleSheetsConnector.defaultOptions = {
id: 'google-sheets-connector',
type: 'GoogleSheets',
googleAPIKey: '',
googleSpreadsheetKey: '',
enablePolling: false,
dataRefreshRate: 2,
firstRowAsNames: true
};
/* *
*
* Constants
*
* */
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
/* *
*
* Functions
*
* */
/**
* Creates GoogleSheets API v4 URL.
* @private
*/
export function buildFetchURL(apiKey, sheetKey, options = {}) {
const url = new URL(`https://sheets.googleapis.com/v4/spreadsheets/${sheetKey}/values/`);
const range = options.onlyColumnIds ?
'A1:Z1' : buildQueryRange(options);
url.pathname += range;
const searchParams = url.searchParams;
searchParams.set('alt', 'json');
if (!options.onlyColumnIds) {
searchParams.set('dateTimeRenderOption', 'FORMATTED_STRING');
searchParams.set('majorDimension', 'COLUMNS');
searchParams.set('valueRenderOption', 'UNFORMATTED_VALUE');
}
searchParams.set('prettyPrint', 'false');
searchParams.set('key', apiKey);
return url.href;
}
/**
* Creates sheets range.
* @private
*/
export function buildQueryRange(options = {}) {
const { endColumn, endRow, googleSpreadsheetRange, startColumn, startRow } = options;
return googleSpreadsheetRange || ((alphabet[startColumn || 0] || 'A') +
(Math.max((startRow || 0), 0) + 1) +
':' +
(alphabet[pick(endColumn, 25)] || 'Z') +
(endRow ?
Math.max(endRow, 0) :
'Z'));
}
/* *
*
* Registry
*
* */
DataConnector.registerType('GoogleSheets', GoogleSheetsConnector);
/* *
*
* Default Export
*
* */
export default GoogleSheetsConnector;