UNPKG

google-ads-api

Version:

Google Ads API Client Library for Node.js

107 lines (106 loc) 4.31 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ParsingError = void 0; exports.parse = parse; exports.getGAQLFields = getGAQLFields; exports.getReportOptionFields = getReportOptionFields; exports.parseRows = parseRows; const long_1 = __importDefault(require("long")); const protos_1 = require("./protos"); const utils_1 = require("./utils"); exports.ParsingError = { NO_REPORT_OPTIONS_OR_GAQL_QUERY: "Must provided reportOptions or gaqlString to parse results.", NO_FIELDS_IN_GAQL_QUERY: "GAQL Query must contain at least one attribute, metric or segment.", NO_FIELDS_IN_REPORT_OPTIONS: "Report Options must contain at least one attribute, metric or segment.", }; /** @description Parse the results of a query @example const parsedResults = parse({ results, reportOptions }) const parsedResults = parse({ results, gaqlString }) */ function parse({ results, reportOptions, gaqlString, }) { if (results.length === 0) { return results; } if (typeof reportOptions === "undefined" && typeof gaqlString === "undefined") { throw new Error(exports.ParsingError.NO_REPORT_OPTIONS_OR_GAQL_QUERY); } const queryFields = reportOptions ? getReportOptionFields(reportOptions) : getGAQLFields(gaqlString); // Add in all relevant resource_name fields, which are always returned by API const entities = queryFields.map((field) => field.split(".")[0]); const resourceNameFields = protos_1.fields.resourceNames.filter((resourceNameField) => entities.includes(resourceNameField.split(".")[0])); const allFields = [...queryFields, ...resourceNameFields]; return parseRows(results, allFields); } // This function assumes that a gaql query is of the format "select * * * from * ...". // Queries that are not in this format should have thrown an error when called. function getGAQLFields(gaqlString) { const normalisedQuery = (0, utils_1.normaliseQuery)(gaqlString); const fields = normalisedQuery .toLowerCase() .replace(/(^\s*select)|( from .*)|(\s+)/g, "") .split(",") .filter((field) => field.length > 0); if (!fields.length) { throw new Error(exports.ParsingError.NO_FIELDS_IN_GAQL_QUERY); } return fields; } function getReportOptionFields(reportOptions) { const fields = [ ...(reportOptions.attributes || []), ...(reportOptions.metrics || []), ...(reportOptions.segments || []), ]; if (!fields.length) { throw new Error(exports.ParsingError.NO_FIELDS_IN_REPORT_OPTIONS); } return fields; } function parseRows(rows, fields) { const fieldsPreSplit = {}; // pre-split all the field strings for performance reasons (increases speed by ~5x for large number of rows) for (const field of fields) { fieldsPreSplit[field] = field.split("."); } const newRows = []; for (let r = 0; r < rows.length; r++) { const newRow = {}; const originalRow = protos_1.services.GoogleAdsRow.fromObject(rows[r]); for (const split in fieldsPreSplit) { // @ts-expect-error These are the best we can do for these types const [parent, ...children] = fieldsPreSplit[split]; // Ignore null fields (unspecified resource names) if (!originalRow[parent]) { continue; } newRow[parent] = parseNestedValues(newRow[parent], originalRow[parent], parent, children); } newRows.push(newRow); } return newRows; } function parseNestedValues(row, data, field, paths) { if (!data) return null; const [parentField, ...childFields] = paths; if (!row) row = {}; if (childFields.length === 0) { const rawVal = data[parentField]; const parsedVal = long_1.default.isLong(rawVal) ? new long_1.default(rawVal.low, rawVal.high, rawVal.unsigned).toNumber() : rawVal; row[parentField] = parsedVal; return row; } row[parentField] = parseNestedValues(row[parentField], data[parentField], parentField, childFields); return row; }