UNPKG

highcharts

Version:
212 lines (211 loc) 5.69 kB
/* * * * (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 * * */ 'use strict'; 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;