UNPKG

ghost

Version:

The professional publishing platform

156 lines (140 loc) 5.83 kB
const logging = require('@tryghost/logging'); /** * Creates a configured Tinybird client * @param {object} deps - Configuration and request dependencies * @param {object} deps.config - Ghost configuration * @param {object} deps.request - HTTP request client * @param {object} deps.settingsCache - Settings cache client * @param {object} deps.tinybirdService - Tinybird service client * @returns {Object} Tinybird client with methods */ const create = ({config, request, settingsCache, tinybirdService}) => { /** * Builds a Tinybird API request * @param {string} pipeName - The name of the Tinybird pipe to query * @param {Object} options - Request options in camelCase format * @param {string} [options.dateFrom] - Start date in YYYY-MM-DD format * @param {string} [options.dateTo] - End date in YYYY-MM-DD format * @param {string} [options.timezone] - Timezone for the query * @param {string} [options.memberStatus] - Member status filter (defaults to 'all') * @param {string} [options.postType] - Post type filter * @param {string} [options.tbVersion] - Tinybird version for API URL * @returns {Object} Object with URL and request options */ const buildRequest = (pipeName, options = {}) => { const statsConfig = config.get('tinybird:stats'); // Use tinybird:stats:id if provided, otherwise use site_uuid from settings cache // Allows overriding site_uuid via config // This is temporary until we have a proper way to use mock data locally const siteUuid = statsConfig.id || settingsCache.get('site_uuid'); const localEnabled = statsConfig?.local?.enabled ?? false; const endpoint = localEnabled ? statsConfig.local.endpoint : statsConfig.endpoint; const tokenData = tinybirdService.getToken(); const token = tokenData?.token; // Use tbVersion if provided for constructing the URL const pipeUrl = (options.tbVersion && !localEnabled) ? `/v0/pipes/${pipeName}__v${options.tbVersion}.json` : `/v0/pipes/${pipeName}.json`; const tinybirdUrl = `${endpoint}${pipeUrl}`; // Use snake_case for query parameters as expected by Tinybird API const searchParams = { site_uuid: siteUuid }; // todo: refactor all uses to simply pass options through if (options.dateFrom) { searchParams.date_from = options.dateFrom; } if (options.dateTo) { searchParams.date_to = options.dateTo; } if (options.timezone) { searchParams.timezone = options.timezone; } if (options.memberStatus) { searchParams.member_status = options.memberStatus; } if (options.postType) { searchParams.post_type = options.postType; } // Add any other options that might be needed Object.entries(options).forEach(([key, value]) => { if (!['dateFrom', 'dateTo', 'timezone', 'memberStatus', 'postType', 'tbVersion'].includes(key)) { // Handle arrays by converting them to comma-separated strings for Tinybird if (Array.isArray(value)) { searchParams[key] = value.join(','); } else { searchParams[key] = value; } } }); // Convert searchParams to query string and append to URL const queryString = new URLSearchParams(searchParams).toString(); const fullUrl = `${tinybirdUrl}?${queryString}`; const requestOptions = { headers: { Authorization: `Bearer ${token}` }, timeout: { request: 10000 } }; return {url: fullUrl, options: requestOptions}; }; /** * Parse response data from Tinybird * @param {Object|string} response - Response from Tinybird API * @returns {Array|null} Parsed data array or null on error */ const parseResponse = (response) => { let responseData; if (response.body) { if (typeof response.body === 'string') { try { responseData = JSON.parse(response.body); } catch (e) { logging.error(`Error parsing response body: ${e.message}`); return null; } } else { responseData = response.body; } } else if (typeof response === 'string') { try { responseData = JSON.parse(response); } catch (e) { logging.error(`Error parsing response string: ${e.message}`); return null; } } else { responseData = response; } if (!responseData || !responseData.data) { return null; } return responseData.data; }; /** * Fetch data from a Tinybird pipe * @param {string} pipeName - The name of the Tinybird pipe to query * @param {Object} options - Request options * @returns {Promise<Array|null>} Parsed data array or null on error */ const fetch = async (pipeName, options = {}) => { const {url, options: requestOptions} = buildRequest(pipeName, options); try { const response = await request.get(url, requestOptions); return parseResponse(response); } catch (error) { logging.error(`Error in Tinybird API request to ${url}:`, error); return null; } }; return { buildRequest, parseResponse, fetch }; }; module.exports = { create };