google-ads-api 24.1.0 → 25.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/build/{src → cjs}/client.d.ts +6 -4
- package/build/{src → cjs}/client.js +4 -4
- package/build/{src → cjs}/customer.d.ts +5 -5
- package/build/{src → cjs}/customer.js +24 -20
- package/build/{src → cjs}/hooks.d.ts +2 -2
- package/build/cjs/index.d.ts +9 -0
- package/build/{src → cjs}/index.js +22 -20
- package/build/{src → cjs}/parser.d.ts +2 -2
- package/build/{src → cjs}/parser.js +5 -5
- package/build/{src → cjs}/parserRest.js +7 -7
- package/build/{src → cjs}/protos/autogen/enums.d.ts +1679 -1180
- package/build/{src → cjs}/protos/autogen/enums.js +1696 -1177
- package/build/cjs/protos/autogen/fields.d.ts +1191 -0
- package/build/cjs/protos/autogen/fields.js +205 -0
- package/build/cjs/protos/autogen/resourceNames.d.ts +1668 -0
- package/build/cjs/protos/autogen/resourceNames.js +1669 -0
- package/build/{src → cjs}/protos/autogen/serviceFactory.d.ts +206 -215
- package/build/{src → cjs}/protos/autogen/serviceFactory.js +483 -554
- package/build/cjs/protos/index.d.ts +14 -0
- package/build/{src → cjs}/protos/index.js +12 -12
- package/build/{src → cjs}/query.d.ts +2 -2
- package/build/{src → cjs}/query.js +5 -5
- package/build/{src → cjs}/service.d.ts +5 -5
- package/build/{src → cjs}/service.js +64 -22
- package/build/{src → cjs}/types.d.ts +1 -1
- package/build/{src → cjs}/utils.d.ts +1 -1
- package/build/{src → cjs}/utils.js +2 -2
- package/build/cjs/version.d.ts +1 -0
- package/build/{src → cjs}/version.js +1 -1
- package/build/esm/client.d.ts +19 -0
- package/build/esm/client.js +40 -0
- package/build/esm/customer.d.ts +71 -0
- package/build/esm/customer.js +502 -0
- package/build/esm/hooks.d.ts +160 -0
- package/build/esm/hooks.js +1 -0
- package/build/esm/index.d.ts +9 -0
- package/build/esm/index.js +10 -0
- package/build/esm/package.json +1 -0
- package/build/esm/parser.d.ts +21 -0
- package/build/esm/parser.js +113 -0
- package/build/esm/parserRest.d.ts +1 -0
- package/build/esm/parserRest.js +112 -0
- package/build/esm/protos/autogen/enums.d.ts +6002 -0
- package/build/esm/protos/autogen/enums.js +6401 -0
- package/build/esm/protos/autogen/fields.d.ts +1191 -0
- package/build/esm/protos/autogen/fields.js +202 -0
- package/build/esm/protos/autogen/resourceNames.d.ts +1668 -0
- package/build/esm/protos/autogen/resourceNames.js +1475 -0
- package/build/esm/protos/autogen/serviceFactory.d.ts +1825 -0
- package/build/esm/protos/autogen/serviceFactory.js +18795 -0
- package/build/esm/protos/index.d.ts +14 -0
- package/build/esm/protos/index.js +19 -0
- package/build/esm/query.d.ts +62 -0
- package/build/esm/query.js +304 -0
- package/build/esm/service.d.ts +47 -0
- package/build/esm/service.js +222 -0
- package/build/esm/types.d.ts +75 -0
- package/build/esm/types.js +14 -0
- package/build/esm/utils.d.ts +49 -0
- package/build/esm/utils.js +108 -0
- package/build/esm/version.d.ts +1 -0
- package/build/esm/version.js +1 -0
- package/package.json +50 -11
- package/build/src/index.d.ts +0 -8
- package/build/src/protos/autogen/fields.d.ts +0 -1122
- package/build/src/protos/autogen/fields.js +0 -644
- package/build/src/protos/autogen/resourceNames.d.ts +0 -1609
- package/build/src/protos/autogen/resourceNames.js +0 -1982
- package/build/src/protos/index.d.ts +0 -14
- package/build/src/testUtils.d.ts +0 -107
- package/build/src/testUtils.js +0 -333
- package/build/src/version.d.ts +0 -1
- /package/build/{src → cjs}/hooks.js +0 -0
- /package/build/{src → cjs}/parserRest.d.ts +0 -0
- /package/build/{src → cjs}/types.js +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReportOptions } from "./types.js";
|
|
2
|
+
import { services } from "./protos/index.js";
|
|
3
|
+
export declare const ParsingError: {
|
|
4
|
+
NO_REPORT_OPTIONS_OR_GAQL_QUERY: string;
|
|
5
|
+
NO_FIELDS_IN_GAQL_QUERY: string;
|
|
6
|
+
NO_FIELDS_IN_REPORT_OPTIONS: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
@description Parse the results of a query
|
|
10
|
+
@example
|
|
11
|
+
const parsedResults = parse({ results, reportOptions })
|
|
12
|
+
const parsedResults = parse({ results, gaqlString })
|
|
13
|
+
*/
|
|
14
|
+
export declare function parse({ results, reportOptions, gaqlString, }: {
|
|
15
|
+
results: services.IGoogleAdsRow[];
|
|
16
|
+
reportOptions?: ReportOptions;
|
|
17
|
+
gaqlString?: string;
|
|
18
|
+
}): services.IGoogleAdsRow[];
|
|
19
|
+
export declare function getGAQLFields(gaqlString: string): string[];
|
|
20
|
+
export declare function getReportOptionFields(reportOptions: ReportOptions): string[];
|
|
21
|
+
export declare function parseRows(rows: services.IGoogleAdsRow[], fields: string[]): services.IGoogleAdsRow[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import long from "long";
|
|
2
|
+
import { services, fields } from "./protos/index.js";
|
|
3
|
+
import { normaliseQuery } from "./utils.js";
|
|
4
|
+
export const ParsingError = {
|
|
5
|
+
NO_REPORT_OPTIONS_OR_GAQL_QUERY: "Must provided reportOptions or gaqlString to parse results.",
|
|
6
|
+
NO_FIELDS_IN_GAQL_QUERY: "GAQL Query must contain at least one attribute, metric or segment.",
|
|
7
|
+
NO_FIELDS_IN_REPORT_OPTIONS: "Report Options must contain at least one attribute, metric or segment.",
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
@description Parse the results of a query
|
|
11
|
+
@example
|
|
12
|
+
const parsedResults = parse({ results, reportOptions })
|
|
13
|
+
const parsedResults = parse({ results, gaqlString })
|
|
14
|
+
*/
|
|
15
|
+
export function parse({ results, reportOptions, gaqlString, }) {
|
|
16
|
+
if (results.length === 0) {
|
|
17
|
+
return results;
|
|
18
|
+
}
|
|
19
|
+
if (typeof reportOptions === "undefined" &&
|
|
20
|
+
typeof gaqlString === "undefined") {
|
|
21
|
+
throw new Error(ParsingError.NO_REPORT_OPTIONS_OR_GAQL_QUERY);
|
|
22
|
+
}
|
|
23
|
+
const queryFields = reportOptions
|
|
24
|
+
? getReportOptionFields(reportOptions)
|
|
25
|
+
: getGAQLFields(gaqlString);
|
|
26
|
+
// Add in all relevant resource_name fields, which are always returned by API
|
|
27
|
+
const entities = queryFields.map((field) => field.split(".")[0]);
|
|
28
|
+
const resourceNameFields = fields.resourceNames.filter((resourceNameField) => entities.includes(resourceNameField.split(".")[0]));
|
|
29
|
+
const allFields = [...queryFields, ...resourceNameFields];
|
|
30
|
+
return parseRows(results, allFields);
|
|
31
|
+
}
|
|
32
|
+
// This function assumes that a gaql query is of the format "select * * * from * ...".
|
|
33
|
+
// Queries that are not in this format should have thrown an error when called.
|
|
34
|
+
export function getGAQLFields(gaqlString) {
|
|
35
|
+
const normalisedQuery = normaliseQuery(gaqlString);
|
|
36
|
+
const fields = normalisedQuery
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.replace(/(^\s*select)|( from .*)|(\s+)/g, "")
|
|
39
|
+
.split(",")
|
|
40
|
+
.filter((field) => field.length > 0);
|
|
41
|
+
if (!fields.length) {
|
|
42
|
+
throw new Error(ParsingError.NO_FIELDS_IN_GAQL_QUERY);
|
|
43
|
+
}
|
|
44
|
+
return fields;
|
|
45
|
+
}
|
|
46
|
+
export function getReportOptionFields(reportOptions) {
|
|
47
|
+
const fields = [
|
|
48
|
+
...(reportOptions.attributes || []),
|
|
49
|
+
...(reportOptions.metrics || []),
|
|
50
|
+
...(reportOptions.segments || []),
|
|
51
|
+
];
|
|
52
|
+
if (!fields.length) {
|
|
53
|
+
throw new Error(ParsingError.NO_FIELDS_IN_REPORT_OPTIONS);
|
|
54
|
+
}
|
|
55
|
+
return fields;
|
|
56
|
+
}
|
|
57
|
+
export function parseRows(rows, fields) {
|
|
58
|
+
const fieldsPreSplit = {};
|
|
59
|
+
// pre-split all the field strings for performance reasons (increases speed by ~5x for large number of rows)
|
|
60
|
+
for (const field of fields) {
|
|
61
|
+
fieldsPreSplit[field] = field.split(".");
|
|
62
|
+
}
|
|
63
|
+
const newRows = [];
|
|
64
|
+
for (let r = 0; r < rows.length; r++) {
|
|
65
|
+
const newRow = {};
|
|
66
|
+
const originalRow = services.GoogleAdsRow.fromObject(rows[r]);
|
|
67
|
+
for (const split in fieldsPreSplit) {
|
|
68
|
+
// @ts-expect-error These are the best we can do for these types
|
|
69
|
+
const [parent, ...children] = fieldsPreSplit[split];
|
|
70
|
+
// Ignore null fields (unspecified resource names)
|
|
71
|
+
if (!originalRow[parent]) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
newRow[parent] = parseNestedValues(newRow[parent], originalRow[parent], parent, children);
|
|
75
|
+
}
|
|
76
|
+
newRows.push(newRow);
|
|
77
|
+
}
|
|
78
|
+
return newRows;
|
|
79
|
+
}
|
|
80
|
+
function convertLongsToNumbers(value) {
|
|
81
|
+
const isPrimitive = value === null || typeof value !== "object";
|
|
82
|
+
if (isPrimitive) {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (long.isLong(value)) {
|
|
86
|
+
return value.toNumber();
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
return value.map(convertLongsToNumbers);
|
|
90
|
+
}
|
|
91
|
+
const isBytesField = ArrayBuffer.isView(value);
|
|
92
|
+
if (isBytesField) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
const converted = {};
|
|
96
|
+
for (const [key, nestedValue] of Object.entries(value)) {
|
|
97
|
+
converted[key] = convertLongsToNumbers(nestedValue);
|
|
98
|
+
}
|
|
99
|
+
return converted;
|
|
100
|
+
}
|
|
101
|
+
function parseNestedValues(row, data, field, paths) {
|
|
102
|
+
if (!data)
|
|
103
|
+
return null;
|
|
104
|
+
const [parentField, ...childFields] = paths;
|
|
105
|
+
if (!row)
|
|
106
|
+
row = {};
|
|
107
|
+
if (childFields.length === 0) {
|
|
108
|
+
row[parentField] = convertLongsToNumbers(data[parentField]);
|
|
109
|
+
return row;
|
|
110
|
+
}
|
|
111
|
+
row[parentField] = parseNestedValues(row[parentField], data[parentField], parentField, childFields);
|
|
112
|
+
return row;
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const decamelizeKeys: (input: any) => any;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { parse } from "circ-json";
|
|
2
|
+
import { toSnakeCase } from "./utils.js";
|
|
3
|
+
import { fieldDataTypes as fieldDataTypesString, fields, } from "./protos/autogen/fields.js";
|
|
4
|
+
import { enums } from "./protos/autogen/enums.js";
|
|
5
|
+
const fieldDataTypes = parse(fieldDataTypesString);
|
|
6
|
+
const decamelizeCache = new Map();
|
|
7
|
+
const fieldTypeCache = new Map();
|
|
8
|
+
const isObject = (value) => typeof value === "object" && value !== null;
|
|
9
|
+
export const decamelizeKeys = (input) => {
|
|
10
|
+
if (!isObject(input)) {
|
|
11
|
+
return input;
|
|
12
|
+
}
|
|
13
|
+
return transform(input);
|
|
14
|
+
function transform(value, parentPath) {
|
|
15
|
+
if (!isObject(value)) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
const length = value.length;
|
|
20
|
+
const result = new Array(length);
|
|
21
|
+
for (let i = 0; i < length; i += 1) {
|
|
22
|
+
const item = value[i];
|
|
23
|
+
result[i] = isObject(item) ? transformObject(item, parentPath) : item;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
return transformObject(value, parentPath);
|
|
28
|
+
}
|
|
29
|
+
function transformObject(obj, parentPath) {
|
|
30
|
+
const output = {};
|
|
31
|
+
for (const key of Object.keys(obj)) {
|
|
32
|
+
const rawValue = obj[key];
|
|
33
|
+
const newKey = cachedDecamelize(key);
|
|
34
|
+
if (isObject(rawValue)) {
|
|
35
|
+
const nextParentPath = parentPath === undefined ? newKey : `${parentPath}.${newKey}`;
|
|
36
|
+
output[newKey] = transform(rawValue, nextParentPath);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
output[newKey] = cachedValueParser(newKey, parentPath, rawValue);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return output;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const cachedDecamelize = (key) => {
|
|
46
|
+
const cachedResult = decamelizeCache.get(key);
|
|
47
|
+
if (cachedResult) {
|
|
48
|
+
return cachedResult;
|
|
49
|
+
}
|
|
50
|
+
const newKey = toSnakeCase(key);
|
|
51
|
+
decamelizeCache.set(key, newKey);
|
|
52
|
+
return newKey;
|
|
53
|
+
};
|
|
54
|
+
const cachedValueParser = (key, parentPath, value) => {
|
|
55
|
+
let newValue = value;
|
|
56
|
+
const fullPath = parentPath ? `${parentPath}.${key}` : key;
|
|
57
|
+
const megaDataType = getTypeFromPath(fullPath);
|
|
58
|
+
if (megaDataType === undefined && !fullPath.startsWith("@")) {
|
|
59
|
+
console.warn(`No data type found for ${fullPath}`);
|
|
60
|
+
}
|
|
61
|
+
else if (typeof megaDataType === "object") {
|
|
62
|
+
// Special handling for FieldMask types - REST API returns them as comma-separated strings
|
|
63
|
+
if (megaDataType.paths === "STRING" && typeof value === "string") {
|
|
64
|
+
// This is a FieldMask field, convert the comma-separated string to the expected format
|
|
65
|
+
// Also convert each path from camelCase to snake_case
|
|
66
|
+
newValue = {
|
|
67
|
+
paths: value.split(",").map((p) => {
|
|
68
|
+
// Handle nested paths like "ipBlock.ipAddress"
|
|
69
|
+
return p
|
|
70
|
+
.trim()
|
|
71
|
+
.split(".")
|
|
72
|
+
.map((segment) => toSnakeCase(segment))
|
|
73
|
+
.join(".");
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Normal enum handling
|
|
79
|
+
newValue = megaDataType[value];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (megaDataType === "INT64") {
|
|
83
|
+
newValue = Number(value);
|
|
84
|
+
}
|
|
85
|
+
else if (megaDataType === "ENUM") {
|
|
86
|
+
// Some enums aren't embedded in megaDataType, so we need this fallback.
|
|
87
|
+
// @ts-expect-error typescript doesn't like accessing items in a namespace with a string
|
|
88
|
+
newValue = enums[fields.enumFields[fullPath]][value]; // e.g. enums['CampaignStatus'][ENABLED] = "2"
|
|
89
|
+
}
|
|
90
|
+
return newValue;
|
|
91
|
+
};
|
|
92
|
+
const getTypeFromPath = (path) => {
|
|
93
|
+
const cachedResult = fieldTypeCache.get(path);
|
|
94
|
+
if (cachedResult) {
|
|
95
|
+
return cachedResult;
|
|
96
|
+
}
|
|
97
|
+
const t = get(fieldDataTypes, path);
|
|
98
|
+
fieldTypeCache.set(path, t);
|
|
99
|
+
return t;
|
|
100
|
+
};
|
|
101
|
+
// Copied from youmightnotneed.com
|
|
102
|
+
const get = (obj, path) => {
|
|
103
|
+
if (!path)
|
|
104
|
+
return undefined;
|
|
105
|
+
// Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
|
|
106
|
+
// Regex explained: https://regexr.com/58j0k
|
|
107
|
+
const pathArray = path.match(/([^[.\]])+/g);
|
|
108
|
+
if (!pathArray)
|
|
109
|
+
return undefined;
|
|
110
|
+
// Find value
|
|
111
|
+
return pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj);
|
|
112
|
+
};
|