google-ads-api 14.2.0 → 16.0.0-rest-beta1
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 +2 -2
- package/build/src/client.d.ts +1 -0
- package/build/src/client.js +1 -0
- package/build/src/customer.d.ts +21 -0
- package/build/src/customer.js +211 -72
- package/build/src/hooks.d.ts +23 -23
- package/build/src/parserRest.d.ts +4 -0
- package/build/src/parserRest.js +88 -0
- package/build/src/protos/autogen/enums.d.ts +3387 -2921
- package/build/src/protos/autogen/enums.js +874 -375
- package/build/src/protos/autogen/fields.d.ts +244 -170
- package/build/src/protos/autogen/fields.js +59 -9
- package/build/src/protos/autogen/resourceNames.d.ts +282 -178
- package/build/src/protos/autogen/resourceNames.js +149 -21
- package/build/src/protos/autogen/serviceFactory.d.ts +225 -178
- package/build/src/protos/autogen/serviceFactory.js +749 -280
- package/build/src/protos/index.d.ts +9 -9
- package/build/src/protos/index.js +7 -6
- package/build/src/query.d.ts +9 -9
- package/build/src/query.js +1 -1
- package/build/src/service.d.ts +2 -1
- package/build/src/service.js +36 -15
- package/build/src/testUtils.d.ts +39 -0
- package/build/src/testUtils.js +76 -5
- package/build/src/types.d.ts +14 -14
- package/build/src/utils.d.ts +0 -5
- package/build/src/utils.js +1 -16
- package/build/src/version.d.ts +1 -1
- package/build/src/version.js +17 -1
- package/package.json +17 -6
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</p>
|
|
8
8
|
<p align="center">
|
|
9
9
|
<a href="https://developers.google.com/google-ads/api/docs/release-notes">
|
|
10
|
-
<img src="https://img.shields.io/badge/google%20ads-
|
|
10
|
+
<img src="https://img.shields.io/badge/google%20ads-v16.1.0-009688.svg?style=flat-square">
|
|
11
11
|
</a>
|
|
12
12
|
<a href="https://www.npmjs.com/package/google-ads-api">
|
|
13
13
|
<img src="https://img.shields.io/npm/v/google-ads-api.svg?style=flat-square">
|
|
@@ -384,7 +384,7 @@ const adGroupResourceName = 'customers/123/adGroups/456'
|
|
|
384
384
|
const keyword = '24 hour locksmith harlem'
|
|
385
385
|
|
|
386
386
|
const operations: MutateOperation<
|
|
387
|
-
resources.IAdGroupCriterion & { exempt_policy_violation_keys?: google.ads.googleads.
|
|
387
|
+
resources.IAdGroupCriterion & { exempt_policy_violation_keys?: google.ads.googleads.v16.common.IPolicyViolationKey[]}
|
|
388
388
|
>[] = [
|
|
389
389
|
{
|
|
390
390
|
entity: 'ad_group_criterion',
|
package/build/src/client.d.ts
CHANGED
package/build/src/client.js
CHANGED
package/build/src/customer.d.ts
CHANGED
|
@@ -4,6 +4,21 @@ import { Hooks } from "./hooks";
|
|
|
4
4
|
import { services } from "./protos";
|
|
5
5
|
import ServiceFactory from "./protos/autogen/serviceFactory";
|
|
6
6
|
import { CustomerOptions, MutateOperation, MutateOptions, ReportOptions, RequestOptions } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* TODO:
|
|
9
|
+
* - rename querierRest to streamRest. Add hooks support to it. OK
|
|
10
|
+
* - write querierRest, using pagination. OK
|
|
11
|
+
* - make sure return_total_results_count works
|
|
12
|
+
* - make sure SUMMARY_ROW_SETTING works
|
|
13
|
+
* - adapt query()
|
|
14
|
+
* - adapt queryStream
|
|
15
|
+
* - adapt report()
|
|
16
|
+
* - adapt reportCount()
|
|
17
|
+
* - adapt reportStream()
|
|
18
|
+
* - reportStreamRaw() ?
|
|
19
|
+
* - bonus: use queryStream when the params allow it. Note: do it with native async generator to make it as fast as possible.
|
|
20
|
+
* - bonus: make it possible to cancel a stream or pagination when row count exceeds specified max
|
|
21
|
+
*/
|
|
7
22
|
export declare class Customer extends ServiceFactory {
|
|
8
23
|
constructor(clientOptions: ClientOptions, customerOptions: CustomerOptions, hooks?: Hooks);
|
|
9
24
|
/**
|
|
@@ -52,8 +67,10 @@ export declare class Customer extends ServiceFactory {
|
|
|
52
67
|
reportStreamRaw(reportOptions: Readonly<ReportOptions>): Promise<CancellableStream | void>;
|
|
53
68
|
private search;
|
|
54
69
|
private paginatedSearch;
|
|
70
|
+
private useStreamToImitateRegularSearch;
|
|
55
71
|
private querier;
|
|
56
72
|
private streamer;
|
|
73
|
+
private handleStreamError;
|
|
57
74
|
/**
|
|
58
75
|
* @description Creates, updates, or removes resources. This method supports atomic transactions
|
|
59
76
|
* with multiple types of resources. For example, you can atomically create a campaign and a
|
|
@@ -62,4 +79,8 @@ export declare class Customer extends ServiceFactory {
|
|
|
62
79
|
*/
|
|
63
80
|
mutateResources<T>(mutations: MutateOperation<T>[], mutateOptions?: MutateOptions): Promise<services.MutateGoogleAdsResponse>;
|
|
64
81
|
private get googleAdsFields();
|
|
82
|
+
private prepareGoogleAdsServicePostRequestArgs;
|
|
83
|
+
private decamelizeKeysIfNeeded;
|
|
84
|
+
private gaqlQueryStringIncludesLimit;
|
|
85
|
+
private generateTooManyRowsError;
|
|
65
86
|
}
|
package/build/src/customer.js
CHANGED
|
@@ -4,13 +4,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Customer = void 0;
|
|
7
|
-
const
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const stream_chain_1 = require("stream-chain");
|
|
9
|
+
const stream_json_1 = require("stream-json");
|
|
10
|
+
const StreamArray_1 = require("stream-json/streamers/StreamArray");
|
|
11
|
+
const parserRest_1 = require("./parserRest");
|
|
12
|
+
const protos_1 = require("./protos");
|
|
8
13
|
const serviceFactory_1 = __importDefault(require("./protos/autogen/serviceFactory"));
|
|
9
14
|
const query_1 = require("./query");
|
|
10
|
-
const
|
|
15
|
+
const version_1 = require("./version");
|
|
16
|
+
const ROWS_PER_STREAMED_CHUNK = 10_000; // From experience, this is what can be expected from the API.
|
|
17
|
+
/**
|
|
18
|
+
* TODO:
|
|
19
|
+
* - rename querierRest to streamRest. Add hooks support to it. OK
|
|
20
|
+
* - write querierRest, using pagination. OK
|
|
21
|
+
* - make sure return_total_results_count works
|
|
22
|
+
* - make sure SUMMARY_ROW_SETTING works
|
|
23
|
+
* - adapt query()
|
|
24
|
+
* - adapt queryStream
|
|
25
|
+
* - adapt report()
|
|
26
|
+
* - adapt reportCount()
|
|
27
|
+
* - adapt reportStream()
|
|
28
|
+
* - reportStreamRaw() ?
|
|
29
|
+
* - bonus: use queryStream when the params allow it. Note: do it with native async generator to make it as fast as possible.
|
|
30
|
+
* - bonus: make it possible to cancel a stream or pagination when row count exceeds specified max
|
|
31
|
+
*/
|
|
11
32
|
class Customer extends serviceFactory_1.default {
|
|
12
33
|
constructor(clientOptions, customerOptions, hooks) {
|
|
13
|
-
super(clientOptions, customerOptions, hooks
|
|
34
|
+
super(clientOptions, customerOptions, hooks ?? {});
|
|
14
35
|
}
|
|
15
36
|
/**
|
|
16
37
|
@description Single query using a raw GAQL string.
|
|
@@ -111,39 +132,118 @@ class Customer extends serviceFactory_1.default {
|
|
|
111
132
|
});
|
|
112
133
|
}
|
|
113
134
|
async search(gaqlQuery, requestOptions) {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
const accessToken = await this.getAccessToken();
|
|
136
|
+
try {
|
|
137
|
+
const rawResponse = await (0, axios_1.default)(this.prepareGoogleAdsServicePostRequestArgs("search", accessToken, {
|
|
138
|
+
data: {
|
|
139
|
+
query: gaqlQuery,
|
|
140
|
+
...requestOptions,
|
|
141
|
+
},
|
|
142
|
+
}));
|
|
143
|
+
const searchResponse = rawResponse.data;
|
|
144
|
+
const results = searchResponse.results ?? [];
|
|
145
|
+
const response = results.map((row) => this.decamelizeKeysIfNeeded(row));
|
|
146
|
+
const summaryRow = this.decamelizeKeysIfNeeded(searchResponse.summaryRow);
|
|
147
|
+
const nextPageToken = searchResponse.nextPageToken;
|
|
148
|
+
const totalResultsCount = searchResponse.totalResultsCount
|
|
149
|
+
? +searchResponse.totalResultsCount
|
|
150
|
+
: undefined;
|
|
151
|
+
return { response, nextPageToken, totalResultsCount, summaryRow };
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (e.response?.data.error.details[0]) {
|
|
155
|
+
throw new protos_1.errors.GoogleAdsFailure(this.decamelizeKeysIfNeeded(e.response.data.error.details[0]));
|
|
156
|
+
}
|
|
157
|
+
throw e;
|
|
127
158
|
}
|
|
128
|
-
return { response, nextPageToken, totalResultsCount };
|
|
129
159
|
}
|
|
130
|
-
async paginatedSearch(gaqlQuery, requestOptions
|
|
160
|
+
async paginatedSearch(gaqlQuery, requestOptions) {
|
|
161
|
+
/*
|
|
162
|
+
When possible, use the searchStream method to avoid the overhead of pagination.
|
|
163
|
+
*/
|
|
164
|
+
if (requestOptions.page_size === undefined &&
|
|
165
|
+
// @ts-expect-error we do not allow this field in reportOptions, however it is still a valid request option
|
|
166
|
+
requestOptions.return_total_results_count === undefined) {
|
|
167
|
+
// If no pagination or summary options are set, we can use the non-paginated search method.
|
|
168
|
+
const { response } = await this.useStreamToImitateRegularSearch(gaqlQuery, requestOptions);
|
|
169
|
+
return { response };
|
|
170
|
+
}
|
|
131
171
|
const response = [];
|
|
132
172
|
let nextPageToken = undefined;
|
|
133
173
|
const initialSearch = await this.search(gaqlQuery, requestOptions);
|
|
134
174
|
const totalResultsCount = initialSearch.totalResultsCount;
|
|
135
|
-
|
|
175
|
+
let summaryRow = initialSearch.summaryRow;
|
|
176
|
+
response.push(...initialSearch.response);
|
|
136
177
|
nextPageToken = initialSearch.nextPageToken;
|
|
137
178
|
while (nextPageToken) {
|
|
138
179
|
const nextSearch = await this.search(gaqlQuery, {
|
|
139
180
|
...requestOptions,
|
|
140
181
|
page_token: nextPageToken,
|
|
141
182
|
});
|
|
142
|
-
response.push(...
|
|
183
|
+
response.push(...nextSearch.response);
|
|
143
184
|
nextPageToken = nextSearch.nextPageToken;
|
|
185
|
+
if (nextSearch.summaryRow) {
|
|
186
|
+
summaryRow = nextSearch.summaryRow;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (summaryRow) {
|
|
190
|
+
response.unshift(summaryRow);
|
|
144
191
|
}
|
|
145
192
|
return { response, totalResultsCount };
|
|
146
193
|
}
|
|
194
|
+
// Google's searchStream method is faster than search, but it does not support all features.
|
|
195
|
+
// When report() is called, we use searchStream if possible, otherwise we use paginatedSearch.
|
|
196
|
+
// Note that just like `paginatedSearch`, this method accumulates results in memory. Use
|
|
197
|
+
// `reportStream` for a more memory-efficient alternative (at the cost of more CPU usage).
|
|
198
|
+
async useStreamToImitateRegularSearch(gaqlQuery, requestOptions) {
|
|
199
|
+
const accessToken = await this.getAccessToken();
|
|
200
|
+
try {
|
|
201
|
+
const args = this.prepareGoogleAdsServicePostRequestArgs("searchStream", accessToken, {
|
|
202
|
+
responseType: "stream",
|
|
203
|
+
data: {
|
|
204
|
+
query: gaqlQuery,
|
|
205
|
+
...requestOptions,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const response = await (0, axios_1.default)(args);
|
|
209
|
+
const stream = response.data;
|
|
210
|
+
const buffers = [];
|
|
211
|
+
let rowCount = -ROWS_PER_STREAMED_CHUNK;
|
|
212
|
+
for await (const data of stream) {
|
|
213
|
+
if (this.clientOptions.max_reporting_rows &&
|
|
214
|
+
!this.gaqlQueryStringIncludesLimit(gaqlQuery)) {
|
|
215
|
+
// This is a quick-and-dirty way to count rows, but it's good enough for our purposes.
|
|
216
|
+
// We want to avoid using a proper JSON streamer here for performance reasons.
|
|
217
|
+
if (data.toString("utf-8").includes(`results":`)) {
|
|
218
|
+
rowCount += ROWS_PER_STREAMED_CHUNK;
|
|
219
|
+
}
|
|
220
|
+
if (rowCount > this.clientOptions.max_reporting_rows) {
|
|
221
|
+
throw this.generateTooManyRowsError();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
buffers.push(data);
|
|
225
|
+
}
|
|
226
|
+
const asString = Buffer.concat(buffers).toString("utf-8");
|
|
227
|
+
const accumulator = [];
|
|
228
|
+
let foundSummaryRow;
|
|
229
|
+
for (const { results, summaryRow } of JSON.parse(asString)) {
|
|
230
|
+
if (summaryRow) {
|
|
231
|
+
foundSummaryRow = this.decamelizeKeysIfNeeded(summaryRow);
|
|
232
|
+
}
|
|
233
|
+
accumulator.push(...(results ?? []).map((row) => {
|
|
234
|
+
return this.decamelizeKeysIfNeeded(row);
|
|
235
|
+
}));
|
|
236
|
+
if (foundSummaryRow) {
|
|
237
|
+
accumulator.unshift(foundSummaryRow);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return { response: accumulator };
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
await this.handleStreamError(e);
|
|
244
|
+
throw e; // The line above should always throw.
|
|
245
|
+
}
|
|
246
|
+
}
|
|
147
247
|
async querier(gaqlQuery, requestOptions = {}, reportOptions, useHooks = true) {
|
|
148
248
|
const baseHookArguments = {
|
|
149
249
|
credentials: this.credentials,
|
|
@@ -170,14 +270,7 @@ class Customer extends serviceFactory_1.default {
|
|
|
170
270
|
}
|
|
171
271
|
}
|
|
172
272
|
try {
|
|
173
|
-
const
|
|
174
|
-
return this.clientOptions.disable_parsing
|
|
175
|
-
? rows
|
|
176
|
-
: reportOptions
|
|
177
|
-
? (0, parser_1.parse)({ results: rows, reportOptions })
|
|
178
|
-
: (0, parser_1.parse)({ results: rows, gaqlString: gaqlQuery });
|
|
179
|
-
};
|
|
180
|
-
const { response, totalResultsCount } = await this.paginatedSearch(gaqlQuery, requestOptions, parsingWapper);
|
|
273
|
+
const { response, totalResultsCount } = await this.paginatedSearch(gaqlQuery, requestOptions);
|
|
181
274
|
if (this.hooks.onQueryEnd && useHooks) {
|
|
182
275
|
const queryResolution = { resolved: false };
|
|
183
276
|
await this.hooks.onQueryEnd({
|
|
@@ -229,58 +322,79 @@ class Customer extends serviceFactory_1.default {
|
|
|
229
322
|
return;
|
|
230
323
|
}
|
|
231
324
|
}
|
|
232
|
-
const { service, request } = this.buildSearchStreamRequestAndService(gaqlQuery, requestOptions);
|
|
233
|
-
const stream = service.searchStream(request, {
|
|
234
|
-
otherArgs: { headers: this.callHeaders },
|
|
235
|
-
});
|
|
236
|
-
let streamFinished = false;
|
|
237
|
-
const accumulator = [];
|
|
238
|
-
let nextChunk = (0, utils_1.createNextChunkArrivedPromise)();
|
|
239
|
-
stream.on("data", (chunk) => {
|
|
240
|
-
const results = chunk.summary_row ? [chunk.summary_row] : chunk.results;
|
|
241
|
-
const parsedResponse = this.clientOptions.disable_parsing
|
|
242
|
-
? results
|
|
243
|
-
: reportOptions
|
|
244
|
-
? (0, parser_1.parse)({ results, reportOptions })
|
|
245
|
-
: (0, parser_1.parse)({ results, gaqlString: gaqlQuery });
|
|
246
|
-
accumulator.push(...parsedResponse);
|
|
247
|
-
nextChunk.resolve();
|
|
248
|
-
nextChunk = (0, utils_1.createNextChunkArrivedPromise)();
|
|
249
|
-
});
|
|
250
|
-
stream.on("error", (searchError) => {
|
|
251
|
-
nextChunk.reject(searchError);
|
|
252
|
-
});
|
|
253
|
-
stream.on("end", () => {
|
|
254
|
-
streamFinished = true;
|
|
255
|
-
nextChunk.resolve();
|
|
256
|
-
});
|
|
257
325
|
try {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
326
|
+
const accessToken = await this.getAccessToken();
|
|
327
|
+
const args = this.prepareGoogleAdsServicePostRequestArgs("searchStream", accessToken, {
|
|
328
|
+
responseType: "stream",
|
|
329
|
+
data: {
|
|
330
|
+
query: gaqlQuery,
|
|
331
|
+
...requestOptions,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
const response = await (0, axios_1.default)(args);
|
|
335
|
+
const stream = response.data;
|
|
336
|
+
// The options below help to make the stream less CPU intensive.
|
|
337
|
+
const parser = new stream_json_1.Parser({
|
|
338
|
+
streamValues: false,
|
|
339
|
+
streamKeys: false,
|
|
340
|
+
packValues: true,
|
|
341
|
+
packKeys: true,
|
|
342
|
+
});
|
|
343
|
+
const pipeline = (0, stream_chain_1.chain)([stream, parser, (0, StreamArray_1.streamArray)()]);
|
|
344
|
+
let count = 0;
|
|
345
|
+
for await (const data of pipeline) {
|
|
346
|
+
const results = data.value.results ?? [data.value.summaryRow];
|
|
347
|
+
count += results.length;
|
|
348
|
+
if (this.clientOptions.max_reporting_rows &&
|
|
349
|
+
count > this.clientOptions.max_reporting_rows &&
|
|
350
|
+
!this.gaqlQueryStringIncludesLimit(gaqlQuery)) {
|
|
351
|
+
throw this.generateTooManyRowsError();
|
|
265
352
|
}
|
|
266
|
-
|
|
267
|
-
|
|
353
|
+
for (const row of results) {
|
|
354
|
+
const parsed = this.decamelizeKeysIfNeeded(row);
|
|
355
|
+
yield parsed;
|
|
268
356
|
}
|
|
269
357
|
}
|
|
358
|
+
return;
|
|
270
359
|
}
|
|
271
|
-
catch (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
360
|
+
catch (e) {
|
|
361
|
+
try {
|
|
362
|
+
await this.handleStreamError(e);
|
|
363
|
+
}
|
|
364
|
+
catch (_e) {
|
|
365
|
+
if (this.hooks.onStreamError) {
|
|
366
|
+
await this.hooks.onStreamError({
|
|
367
|
+
...baseHookArguments,
|
|
368
|
+
error: _e,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
throw _e;
|
|
278
372
|
}
|
|
279
|
-
throw googleAdsError;
|
|
280
373
|
}
|
|
281
|
-
|
|
282
|
-
|
|
374
|
+
}
|
|
375
|
+
async handleStreamError(e) {
|
|
376
|
+
if (!e?.response?.data) {
|
|
377
|
+
throw e;
|
|
283
378
|
}
|
|
379
|
+
// The error is a stream, so some effort is required to parse it.
|
|
380
|
+
const stream = e.response.data;
|
|
381
|
+
const pipeline = (0, stream_chain_1.chain)([stream, (0, stream_json_1.parser)(), (0, StreamArray_1.streamArray)()]);
|
|
382
|
+
const defaultErrorMessage = "Unknown GoogleAdsFailure";
|
|
383
|
+
let googleAdsFailure = new Error(defaultErrorMessage);
|
|
384
|
+
// Only throw the first error.
|
|
385
|
+
pipeline.once("data", (data) => {
|
|
386
|
+
if (data?.value?.error?.details?.[0]) {
|
|
387
|
+
googleAdsFailure = new protos_1.errors.GoogleAdsFailure(this.decamelizeKeysIfNeeded(data.value.error.details[0]));
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
googleAdsFailure = new Error(data?.value?.error?.message ?? defaultErrorMessage, { cause: data?.value?.error ?? data?.value });
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
// Must always reject.
|
|
394
|
+
await new Promise((_, reject) => {
|
|
395
|
+
pipeline.on("end", () => reject(googleAdsFailure));
|
|
396
|
+
pipeline.on("error", (err) => reject(err));
|
|
397
|
+
});
|
|
284
398
|
}
|
|
285
399
|
/**
|
|
286
400
|
* @description Creates, updates, or removes resources. This method supports atomic transactions
|
|
@@ -360,5 +474,30 @@ class Customer extends serviceFactory_1.default {
|
|
|
360
474
|
},
|
|
361
475
|
};
|
|
362
476
|
}
|
|
477
|
+
prepareGoogleAdsServicePostRequestArgs(functionName, accessToken, extra) {
|
|
478
|
+
return {
|
|
479
|
+
method: "POST",
|
|
480
|
+
url: `https://googleads.googleapis.com/${version_1.googleAdsVersion}/customers/${this.customerOptions.customer_id}/googleAds:${functionName}`,
|
|
481
|
+
headers: {
|
|
482
|
+
Authorization: `Bearer ${accessToken}`,
|
|
483
|
+
...this.callHeaders,
|
|
484
|
+
"developer-token": this.clientOptions.developer_token ?? "",
|
|
485
|
+
"login-customer-id": this.credentials.login_customer_id ?? "",
|
|
486
|
+
},
|
|
487
|
+
...extra,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
decamelizeKeysIfNeeded(input) {
|
|
491
|
+
if (this.clientOptions.disable_parsing) {
|
|
492
|
+
return input;
|
|
493
|
+
}
|
|
494
|
+
return (0, parserRest_1.decamelizeKeys)(input);
|
|
495
|
+
}
|
|
496
|
+
gaqlQueryStringIncludesLimit(gaqlQuery) {
|
|
497
|
+
return gaqlQuery.toLowerCase().includes("limit ");
|
|
498
|
+
}
|
|
499
|
+
generateTooManyRowsError() {
|
|
500
|
+
return new Error(`Exceeded the maximum number of rows set by "max_reporting_rows" (${this.clientOptions.max_reporting_rows}).`);
|
|
501
|
+
}
|
|
363
502
|
}
|
|
364
503
|
exports.Customer = Customer;
|
package/build/src/hooks.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { errors, services } from "./protos";
|
|
2
2
|
import { CustomerCredentials, MutateOperation, ReportOptions, RequestOptions, MutateOptions } from "./types";
|
|
3
|
-
export
|
|
3
|
+
export type BaseRequestHookArgs = {
|
|
4
4
|
credentials: CustomerCredentials;
|
|
5
5
|
query: string;
|
|
6
6
|
reportOptions?: ReportOptions;
|
|
7
7
|
};
|
|
8
|
-
export
|
|
8
|
+
export type BaseMutationHookArgs = {
|
|
9
9
|
credentials: CustomerCredentials;
|
|
10
10
|
method: `${keyof typeof services}.${string}`;
|
|
11
11
|
} & (// Mutation was executed with customer.mutateResources
|
|
@@ -16,37 +16,37 @@ export declare type BaseMutationHookArgs = {
|
|
|
16
16
|
mutation: any;
|
|
17
17
|
isServiceCall: true;
|
|
18
18
|
});
|
|
19
|
-
export
|
|
19
|
+
export type BaseServiceHookArgs = {
|
|
20
20
|
credentials: CustomerCredentials;
|
|
21
21
|
method: `${keyof typeof services}.${string}`;
|
|
22
22
|
requestOptions: any;
|
|
23
23
|
};
|
|
24
|
-
|
|
24
|
+
type StartHookArgs<T = RequestOptions | MutateOptions | any, A = void> = {
|
|
25
25
|
cancel: A extends void ? () => void : (args?: A) => void;
|
|
26
26
|
editOptions: (options: Partial<T>) => void;
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
type ErrorHookArgs = {
|
|
29
29
|
error: errors.GoogleAdsFailure | Error;
|
|
30
30
|
};
|
|
31
|
-
|
|
31
|
+
type EndHookArgs<T = services.IGoogleAdsRow[] | services.MutateGoogleAdsResponse> = {
|
|
32
32
|
response?: T;
|
|
33
33
|
resolve: (args: any) => void;
|
|
34
34
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export
|
|
40
|
-
export
|
|
41
|
-
export
|
|
42
|
-
export
|
|
43
|
-
export
|
|
44
|
-
export
|
|
45
|
-
export
|
|
46
|
-
export
|
|
47
|
-
export
|
|
48
|
-
export
|
|
49
|
-
export
|
|
35
|
+
type HookArgs = StartHookArgs | ErrorHookArgs | EndHookArgs;
|
|
36
|
+
type RequestHook<H extends HookArgs> = (a: BaseRequestHookArgs & H) => void;
|
|
37
|
+
type MutationHook<H extends HookArgs> = (a: BaseMutationHookArgs & H) => void;
|
|
38
|
+
type ServiceHook<H extends HookArgs> = (a: BaseServiceHookArgs & H) => void;
|
|
39
|
+
export type OnQueryStart = RequestHook<StartHookArgs<RequestOptions, any>>;
|
|
40
|
+
export type OnQueryError = RequestHook<ErrorHookArgs>;
|
|
41
|
+
export type OnQueryEnd = RequestHook<EndHookArgs<services.IGoogleAdsRow[]>>;
|
|
42
|
+
export type OnStreamStart = RequestHook<StartHookArgs<RequestOptions, void>>;
|
|
43
|
+
export type OnStreamError = RequestHook<ErrorHookArgs>;
|
|
44
|
+
export type OnMutationStart = MutationHook<StartHookArgs<MutateOptions, any>>;
|
|
45
|
+
export type OnMutationError = MutationHook<ErrorHookArgs>;
|
|
46
|
+
export type OnMutationEnd = MutationHook<EndHookArgs<services.MutateGoogleAdsResponse>>;
|
|
47
|
+
export type OnServiceStart = ServiceHook<StartHookArgs<any, any>>;
|
|
48
|
+
export type OnServiceError = ServiceHook<ErrorHookArgs>;
|
|
49
|
+
export type OnServiceEnd = ServiceHook<EndHookArgs<any>>;
|
|
50
50
|
export interface Hooks {
|
|
51
51
|
/**
|
|
52
52
|
* @description Hook called before execution of a query in the `query` and `report` methods
|
|
@@ -149,11 +149,11 @@ export interface Hooks {
|
|
|
149
149
|
*/
|
|
150
150
|
onServiceEnd?: OnServiceEnd;
|
|
151
151
|
}
|
|
152
|
-
export
|
|
152
|
+
export type HookedCancellation = {
|
|
153
153
|
cancelled: boolean;
|
|
154
154
|
res?: any;
|
|
155
155
|
};
|
|
156
|
-
export
|
|
156
|
+
export type HookedResolution = {
|
|
157
157
|
resolved: boolean;
|
|
158
158
|
res?: any;
|
|
159
159
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JSON Rest parsing
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.decamelizeKeys = void 0;
|
|
10
|
+
const map_obj_1 = __importDefault(require("map-obj"));
|
|
11
|
+
const circ_json_1 = require("circ-json");
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const fields_1 = require("./protos/autogen/fields");
|
|
14
|
+
const enums_1 = require("./protos/autogen/enums");
|
|
15
|
+
const fieldDataTypes = (0, circ_json_1.parse)(fields_1.fieldDataTypes);
|
|
16
|
+
const decamelizeCache = new Map();
|
|
17
|
+
const fieldTypeCache = new Map();
|
|
18
|
+
const isObject = (value) => typeof value === "object" && value !== null;
|
|
19
|
+
const decamelizeKeys = (input) => {
|
|
20
|
+
if (!isObject(input)) {
|
|
21
|
+
return input;
|
|
22
|
+
}
|
|
23
|
+
const makeMapper = (parentPath) => (key, value) => {
|
|
24
|
+
key = cachedDecamelize(key);
|
|
25
|
+
if (isObject(value)) {
|
|
26
|
+
const path = parentPath === undefined ? key : `${parentPath}.${key}`;
|
|
27
|
+
// @ts-ignore
|
|
28
|
+
value = (0, map_obj_1.default)(value, makeMapper(path));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
value = cachedValueParser(key, parentPath, value);
|
|
32
|
+
}
|
|
33
|
+
return [key, value];
|
|
34
|
+
};
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
return (0, map_obj_1.default)(input, makeMapper());
|
|
37
|
+
};
|
|
38
|
+
exports.decamelizeKeys = decamelizeKeys;
|
|
39
|
+
const cachedDecamelize = (key) => {
|
|
40
|
+
const cachedResult = decamelizeCache.get(key);
|
|
41
|
+
if (cachedResult) {
|
|
42
|
+
return cachedResult;
|
|
43
|
+
}
|
|
44
|
+
const newKey = (0, utils_1.toSnakeCase)(key);
|
|
45
|
+
decamelizeCache.set(key, newKey);
|
|
46
|
+
return newKey;
|
|
47
|
+
};
|
|
48
|
+
const cachedValueParser = (key, parentPath, value) => {
|
|
49
|
+
let newValue = value;
|
|
50
|
+
const fullPath = parentPath ? `${parentPath}.${key}` : key;
|
|
51
|
+
const megaDataType = getTypeFromPath(fullPath);
|
|
52
|
+
if (megaDataType === undefined && !fullPath.startsWith("@")) {
|
|
53
|
+
console.warn(`No data type found for ${fullPath}`);
|
|
54
|
+
}
|
|
55
|
+
else if (typeof megaDataType === "object") {
|
|
56
|
+
newValue = megaDataType[value];
|
|
57
|
+
}
|
|
58
|
+
else if (megaDataType === "INT64") {
|
|
59
|
+
newValue = Number(value);
|
|
60
|
+
}
|
|
61
|
+
else if (megaDataType === "ENUM") {
|
|
62
|
+
// Some enums aren't embedded in megaDataType, so we need this fallback.
|
|
63
|
+
// @ts-expect-error typescript doesn't like accessing items in a namespace with a string
|
|
64
|
+
newValue = enums_1.enums[fields_1.fields.enumFields[fullPath]][value]; // e.g. enums['CampaignStatus'][ENABLED] = "2"
|
|
65
|
+
}
|
|
66
|
+
return newValue;
|
|
67
|
+
};
|
|
68
|
+
const getTypeFromPath = (path) => {
|
|
69
|
+
const cachedResult = fieldTypeCache.get(path);
|
|
70
|
+
if (cachedResult) {
|
|
71
|
+
return cachedResult;
|
|
72
|
+
}
|
|
73
|
+
const t = get(fieldDataTypes, path);
|
|
74
|
+
fieldTypeCache.set(path, t);
|
|
75
|
+
return t;
|
|
76
|
+
};
|
|
77
|
+
// Copied from youmightnotneed.com
|
|
78
|
+
const get = (obj, path) => {
|
|
79
|
+
if (!path)
|
|
80
|
+
return undefined;
|
|
81
|
+
// Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
|
|
82
|
+
// Regex explained: https://regexr.com/58j0k
|
|
83
|
+
const pathArray = path.match(/([^[.\]])+/g);
|
|
84
|
+
if (!pathArray)
|
|
85
|
+
return undefined;
|
|
86
|
+
// Find value
|
|
87
|
+
return pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj);
|
|
88
|
+
};
|