@google-cloud/nodejs-common 1.2.0 → 1.2.1-beta
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/bin/install_functions.sh +9 -0
- package/package.json +3 -3
- package/src/apis/google_ads.js +306 -54
package/bin/install_functions.sh
CHANGED
|
@@ -384,6 +384,15 @@ solution installed [${GCP_PROJECT}]: "
|
|
|
384
384
|
local input result
|
|
385
385
|
read -r input
|
|
386
386
|
input=${input:-"${GCP_PROJECT}"}
|
|
387
|
+
printf '%s' "Checking billing status for [${input}]..."
|
|
388
|
+
result=$(gcloud beta billing projects describe "${input}" \
|
|
389
|
+
--format="csv[no-heading](billingEnabled)")
|
|
390
|
+
if [[ "${result}" != "True" && "${result}" != "true" ]]; then
|
|
391
|
+
printf '%s\n' " there is no billing account."
|
|
392
|
+
return 1
|
|
393
|
+
else
|
|
394
|
+
printf '%s\n' "succeeded."
|
|
395
|
+
fi
|
|
387
396
|
result=$(gcloud config set project "${input}" --user-output-enabled=false \
|
|
388
397
|
2>&1)
|
|
389
398
|
if [[ -z ${result} ]]; then
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@google-cloud/nodejs-common",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1-beta",
|
|
4
4
|
"description": "A NodeJs common library for solutions based on Cloud Functions",
|
|
5
5
|
"author": "Google Inc.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"@google-cloud/storage": "^6.0.1",
|
|
27
27
|
"@google-cloud/scheduler": "^3.0.0",
|
|
28
28
|
"gaxios": "^5.0.0",
|
|
29
|
-
"google-ads-api": "^
|
|
30
|
-
"google-ads-node":"^
|
|
29
|
+
"google-ads-api": "^11.0.0",
|
|
30
|
+
"google-ads-node": "^9.0.0",
|
|
31
31
|
"google-auth-library": "^8.0.2",
|
|
32
32
|
"googleapis": "^100.0.0",
|
|
33
33
|
"winston": "^3.7.2",
|
package/src/apis/google_ads.js
CHANGED
|
@@ -27,9 +27,15 @@ const {
|
|
|
27
27
|
},
|
|
28
28
|
resources: {
|
|
29
29
|
GoogleAdsField,
|
|
30
|
+
OfflineUserDataJob,
|
|
30
31
|
},
|
|
31
32
|
services: {
|
|
33
|
+
CreateOfflineUserDataJobRequest,
|
|
34
|
+
AddOfflineUserDataJobOperationsRequest,
|
|
35
|
+
RunOfflineUserDataJobRequest,
|
|
36
|
+
UploadCallConversionsRequest,
|
|
32
37
|
UploadClickConversionsRequest,
|
|
38
|
+
UploadCallConversionsResponse,
|
|
33
39
|
UploadClickConversionsResponse,
|
|
34
40
|
UploadConversionAdjustmentsRequest,
|
|
35
41
|
UploadConversionAdjustmentsResponse,
|
|
@@ -41,6 +47,10 @@ const {
|
|
|
41
47
|
errors: {
|
|
42
48
|
GoogleAdsFailure,
|
|
43
49
|
},
|
|
50
|
+
enums: {
|
|
51
|
+
OfflineUserDataJobTypeEnum: { OfflineUserDataJobType },
|
|
52
|
+
OfflineUserDataJobStatusEnum: { OfflineUserDataJobStatus },
|
|
53
|
+
},
|
|
44
54
|
} = googleAdsLib;
|
|
45
55
|
const {GoogleAdsApi} = require('google-ads-api');
|
|
46
56
|
const lodash = require('lodash');
|
|
@@ -55,6 +65,7 @@ const API_SCOPES = Object.freeze(['https://www.googleapis.com/auth/adwords',]);
|
|
|
55
65
|
* List of properties that will be taken from the data file as elements of a
|
|
56
66
|
* conversion or a conversion adjustment.
|
|
57
67
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/ClickConversion
|
|
68
|
+
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/CallConversion
|
|
58
69
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/ConversionAdjustment
|
|
59
70
|
* @type {Array<string>}
|
|
60
71
|
*/
|
|
@@ -63,6 +74,8 @@ const PICKED_PROPERTIES = [
|
|
|
63
74
|
'cart_data',
|
|
64
75
|
'user_identifiers',
|
|
65
76
|
'gclid',
|
|
77
|
+
'caller_id',
|
|
78
|
+
'call_start_date_time',
|
|
66
79
|
'conversion_action',
|
|
67
80
|
'conversion_date_time',
|
|
68
81
|
'conversion_value',
|
|
@@ -95,10 +108,11 @@ const IDENTIFIERS = [
|
|
|
95
108
|
const MAX_IDENTIFIERS_PER_USER = 20;
|
|
96
109
|
|
|
97
110
|
/**
|
|
98
|
-
* Configuration for uploading click conversions or conversion
|
|
99
|
-
* Google Ads, includes:
|
|
111
|
+
* Configuration for uploading click conversions, call converions or conversion
|
|
112
|
+
* adjustments for Google Ads, includes:
|
|
100
113
|
* gclid, conversion_action, conversion_date_time, conversion_value,
|
|
101
114
|
* currency_code, order_id, external_attribution_data,
|
|
115
|
+
* caller_id, call_start_date_time,
|
|
102
116
|
* adjustment_type, adjustment_date_time, user_agent, gclid_date_time_pair, etc.
|
|
103
117
|
* @see PICKED_PROPERTIES
|
|
104
118
|
*
|
|
@@ -119,7 +133,9 @@ const MAX_IDENTIFIERS_PER_USER = 20;
|
|
|
119
133
|
* @typedef {{
|
|
120
134
|
* external_attribution_data: (GoogleAdsApi.ExternalAttributionData|undefined),
|
|
121
135
|
* cart_data: (object|undefined),
|
|
122
|
-
* gclid: string,
|
|
136
|
+
* gclid: (string|undefined),
|
|
137
|
+
* caller_id: (string|undefined),
|
|
138
|
+
* call_start_date_time: (string|undefined),
|
|
123
139
|
* conversion_action: string,
|
|
124
140
|
* conversion_date_time: string,
|
|
125
141
|
* conversion_value: number,
|
|
@@ -138,7 +154,7 @@ let ConversionConfig;
|
|
|
138
154
|
/**
|
|
139
155
|
* Configuration for uploading customer match to Google Ads, includes:
|
|
140
156
|
* customer_id, login_customer_id, list_id and operation.
|
|
141
|
-
* operation must be one of the two: 'create' or 'remove'
|
|
157
|
+
* operation must be one of the two: 'create' or 'remove'.
|
|
142
158
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserDataOperation
|
|
143
159
|
* @typedef {{
|
|
144
160
|
* customer_id: string,
|
|
@@ -149,6 +165,24 @@ let ConversionConfig;
|
|
|
149
165
|
*/
|
|
150
166
|
let CustomerMatchConfig;
|
|
151
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Configuration for offline user data job, includes:
|
|
170
|
+
* customer_id, login_customer_id, list_id, operation and type.
|
|
171
|
+
* 'operation' should be one of the two: 'create' or 'remove',
|
|
172
|
+
* 'type' is OfflineUserDataJobType, it can be 'CUSTOMER_MATCH_USER_LIST' or
|
|
173
|
+
* 'STORE_SALES_UPLOAD_FIRST_PARTY'.
|
|
174
|
+
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/OfflineUserDataJob
|
|
175
|
+
* @typedef {{
|
|
176
|
+
* customer_id: string,
|
|
177
|
+
* login_customer_id: string,
|
|
178
|
+
* list_id: (undefined|string),
|
|
179
|
+
* operation: 'create'|'remove',
|
|
180
|
+
* type: !OfflineUserDataJobType,
|
|
181
|
+
* storeSalesMetadata: (undefined|object),
|
|
182
|
+
* }}
|
|
183
|
+
*/
|
|
184
|
+
let OfflineUserDataJobConfig;
|
|
185
|
+
|
|
152
186
|
/**
|
|
153
187
|
* Configuration for uploading customer match data for Google Ads.
|
|
154
188
|
* @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserIdentifier
|
|
@@ -215,6 +249,19 @@ class GoogleAds {
|
|
|
215
249
|
this.logger.debug(`Init ${this.constructor.name} with Debug Mode.`);
|
|
216
250
|
}
|
|
217
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Gets a report synchronously from a given Customer account.
|
|
254
|
+
* The enum fields are present as index number.
|
|
255
|
+
* @param {string} customerId
|
|
256
|
+
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
257
|
+
* @param {!ReportQueryConfig} reportQueryConfig
|
|
258
|
+
* @return {!ReadableStream}
|
|
259
|
+
*/
|
|
260
|
+
async getReport(customerId, loginCustomerId, reportQueryConfig) {
|
|
261
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
262
|
+
return customer.report(reportQueryConfig);
|
|
263
|
+
}
|
|
264
|
+
|
|
218
265
|
/**
|
|
219
266
|
* Gets report as generator of a given Customer account.
|
|
220
267
|
* @param {string} customerId
|
|
@@ -261,6 +308,19 @@ class GoogleAds {
|
|
|
261
308
|
.searchGoogleAdsFields(request);
|
|
262
309
|
return results;
|
|
263
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Returns the function to send out a request to Google Ads API with a batch
|
|
313
|
+
* of call conversions.
|
|
314
|
+
* @param {string} customerId
|
|
315
|
+
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
316
|
+
* @param {!ConversionConfig} adsConfig Default call conversion params
|
|
317
|
+
* @return {!SendSingleBatch} Function which can send a batch of hits to
|
|
318
|
+
* Google Ads API.
|
|
319
|
+
*/
|
|
320
|
+
getUploadCallConversionFn(customerId, loginCustomerId, adsConfig) {
|
|
321
|
+
return this.getUploadConversionFnBase_(customerId, loginCustomerId,
|
|
322
|
+
adsConfig, 'uploadCallConversions', 'caller_id');
|
|
323
|
+
}
|
|
264
324
|
|
|
265
325
|
/**
|
|
266
326
|
* Returns the function to send out a request to Google Ads API with a batch
|
|
@@ -271,45 +331,9 @@ class GoogleAds {
|
|
|
271
331
|
* @return {!SendSingleBatch} Function which can send a batch of hits to
|
|
272
332
|
* Google Ads API.
|
|
273
333
|
*/
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
* @param {!Array<string>} lines Data for single request. It should be
|
|
278
|
-
* guaranteed that it doesn't exceed quota limitation.
|
|
279
|
-
* @param {string} batchId The tag for log.
|
|
280
|
-
* @return {!BatchResult}
|
|
281
|
-
*/
|
|
282
|
-
return async (lines, batchId) => {
|
|
283
|
-
/** @type {!Array<ConversionConfig>} */
|
|
284
|
-
const conversions = lines.map(
|
|
285
|
-
(line) => buildClickConversionFromLine(line, adsConfig, customerId));
|
|
286
|
-
/** @const {BatchResult} */
|
|
287
|
-
const batchResult = {
|
|
288
|
-
result: true,
|
|
289
|
-
numberOfLines: lines.length,
|
|
290
|
-
};
|
|
291
|
-
try {
|
|
292
|
-
const response = await this.uploadClickConversions(conversions,
|
|
293
|
-
customerId, loginCustomerId);
|
|
294
|
-
const {results, partial_failure_error: failed} = response;
|
|
295
|
-
if (this.logger.isDebugEnabled()) {
|
|
296
|
-
const gclids = results.map((conversion) => conversion.gclid);
|
|
297
|
-
this.logger.debug('Uploaded gclids:', gclids);
|
|
298
|
-
}
|
|
299
|
-
if (failed) {
|
|
300
|
-
this.logger.info('partial_failure_error:', failed.message);
|
|
301
|
-
const failures = failed.details.map(
|
|
302
|
-
({value}) => GoogleAdsFailure.decode(value));
|
|
303
|
-
this.extraFailedLines_(batchResult, failures, lines, 0);
|
|
304
|
-
}
|
|
305
|
-
return batchResult;
|
|
306
|
-
} catch (error) {
|
|
307
|
-
this.logger.error(
|
|
308
|
-
`Error in upload conversions batch: ${batchId}`, error);
|
|
309
|
-
this.updateBatchResultWithError_(batchResult, error, lines, 0);
|
|
310
|
-
return batchResult;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
334
|
+
getUploadClickConversionFn(customerId, loginCustomerId, adsConfig) {
|
|
335
|
+
return this.getUploadConversionFnBase_(customerId, loginCustomerId,
|
|
336
|
+
adsConfig, 'uploadClickConversions', 'gclid');
|
|
313
337
|
}
|
|
314
338
|
|
|
315
339
|
/**
|
|
@@ -323,6 +347,26 @@ class GoogleAds {
|
|
|
323
347
|
* Google Ads API.
|
|
324
348
|
*/
|
|
325
349
|
getUploadConversionAdjustmentFn(customerId, loginCustomerId, adsConfig) {
|
|
350
|
+
return this.getUploadConversionFnBase_(customerId, loginCustomerId,
|
|
351
|
+
adsConfig, 'uploadConversionAdjustments', 'order_id');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Returns the function to send call conversions, click conversions or
|
|
356
|
+
* conversion adjustment (enhanced conversions).
|
|
357
|
+
* @param {string} customerId
|
|
358
|
+
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
359
|
+
* @param {!ConversionConfig} adsConfig Default click conversion params
|
|
360
|
+
* @param {string} functionName The name of sending converions function, could
|
|
361
|
+
* be `uploadClickConversions`, `uploadCallConversions` or
|
|
362
|
+
* `uploadConversionAdjustments`.
|
|
363
|
+
* @param {string} propertyForDebug The name of property for debug info.
|
|
364
|
+
* @return {!SendSingleBatch} Function which can send a batch of hits to
|
|
365
|
+
* Google Ads API.
|
|
366
|
+
* @private
|
|
367
|
+
*/
|
|
368
|
+
getUploadConversionFnBase_(customerId, loginCustomerId, adsConfig,
|
|
369
|
+
functionName, propertyForDebug) {
|
|
326
370
|
/**
|
|
327
371
|
* Sends a batch of hits to Google Ads API.
|
|
328
372
|
* @param {!Array<string>} lines Data for single request. It should be
|
|
@@ -333,30 +377,30 @@ class GoogleAds {
|
|
|
333
377
|
return async (lines, batchId) => {
|
|
334
378
|
/** @type {!Array<ConversionConfig>} */
|
|
335
379
|
const conversions = lines.map(
|
|
336
|
-
|
|
380
|
+
(line) => buildClickConversionFromLine(line, adsConfig, customerId));
|
|
337
381
|
/** @const {BatchResult} */
|
|
338
382
|
const batchResult = {
|
|
339
383
|
result: true,
|
|
340
384
|
numberOfLines: lines.length,
|
|
341
385
|
};
|
|
342
386
|
try {
|
|
343
|
-
const response = await this
|
|
344
|
-
|
|
345
|
-
const {results, partial_failure_error: failed} = response;
|
|
387
|
+
const response = await this[functionName](conversions, customerId,
|
|
388
|
+
loginCustomerId);
|
|
389
|
+
const { results, partial_failure_error: failed } = response;
|
|
346
390
|
if (this.logger.isDebugEnabled()) {
|
|
347
|
-
const
|
|
348
|
-
this.logger.debug(
|
|
391
|
+
const id = results.map((conversion) => conversion[propertyForDebug]);
|
|
392
|
+
this.logger.debug(`Uploaded ${propertyForDebug}:`, id);
|
|
349
393
|
}
|
|
350
394
|
if (failed) {
|
|
351
395
|
this.logger.info('partial_failure_error:', failed.message);
|
|
352
396
|
const failures = failed.details.map(
|
|
353
|
-
|
|
397
|
+
({ value }) => GoogleAdsFailure.decode(value));
|
|
354
398
|
this.extraFailedLines_(batchResult, failures, lines, 0);
|
|
355
399
|
}
|
|
356
400
|
return batchResult;
|
|
357
401
|
} catch (error) {
|
|
358
402
|
this.logger.error(
|
|
359
|
-
|
|
403
|
+
`Error in ${functionName} batch: ${batchId}`, error);
|
|
360
404
|
this.updateBatchResultWithError_(batchResult, error, lines, 0);
|
|
361
405
|
return batchResult;
|
|
362
406
|
}
|
|
@@ -493,6 +537,27 @@ class GoogleAds {
|
|
|
493
537
|
batchResult.errors = Array.from(errors);
|
|
494
538
|
}
|
|
495
539
|
|
|
540
|
+
/**
|
|
541
|
+
* Uploads call conversions to google ads account.
|
|
542
|
+
* It requires an array of call conversions and customer id.
|
|
543
|
+
* In DEBUG mode, this function will only validate the conversions.
|
|
544
|
+
* @param {Array<ConversionConfig>} callConversions Call Conversions
|
|
545
|
+
* @param {string} customerId
|
|
546
|
+
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
547
|
+
* @return {!Promise<!UploadCallConversionsResponse>}
|
|
548
|
+
*/
|
|
549
|
+
uploadCallConversions(callConversions, customerId, loginCustomerId) {
|
|
550
|
+
this.logger.debug('Upload call conversions for customerId:', customerId);
|
|
551
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
552
|
+
const request = new UploadCallConversionsRequest({
|
|
553
|
+
conversions: callConversions,
|
|
554
|
+
customer_id: customerId,
|
|
555
|
+
validate_only: this.debugMode, // when true makes no changes
|
|
556
|
+
partial_failure: true, // Will still create the non-failed entities
|
|
557
|
+
});
|
|
558
|
+
return customer.conversionUploads.uploadCallConversions(request);
|
|
559
|
+
}
|
|
560
|
+
|
|
496
561
|
/**
|
|
497
562
|
* Uploads click conversions to google ads account.
|
|
498
563
|
* It requires an array of click conversions and customer id.
|
|
@@ -613,9 +678,9 @@ class GoogleAds {
|
|
|
613
678
|
* @return {!Promise<UploadUserDataResponse>}
|
|
614
679
|
*/
|
|
615
680
|
async uploadUserDataToUserList(customerMatchRecords, customerMatchConfig) {
|
|
616
|
-
const customerId = customerMatchConfig.customer_id
|
|
617
|
-
const loginCustomerId =
|
|
618
|
-
|
|
681
|
+
const customerId = this.getCleanCid_(customerMatchConfig.customer_id);
|
|
682
|
+
const loginCustomerId = this.getCleanCid_(
|
|
683
|
+
customerMatchConfig.login_customer_id);
|
|
619
684
|
const userListId = customerMatchConfig.list_id;
|
|
620
685
|
const operation = customerMatchConfig.operation;
|
|
621
686
|
|
|
@@ -688,6 +753,191 @@ class GoogleAds {
|
|
|
688
753
|
});
|
|
689
754
|
}
|
|
690
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Returns a integer format CID by removing dashes.
|
|
758
|
+
* @param {string} cid
|
|
759
|
+
* @return {string}
|
|
760
|
+
* @private
|
|
761
|
+
*/
|
|
762
|
+
getCleanCid_(cid) {
|
|
763
|
+
return cid.replace(/-/g, '');
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Get OfflineUserDataJob status.
|
|
768
|
+
* @param {OfflineUserDataJobConfig} config Offline user data job config.
|
|
769
|
+
* @param {string} resourceName
|
|
770
|
+
* @return {!OfflineUserDataJobStatus} Job status
|
|
771
|
+
*/
|
|
772
|
+
async getOfflineUserDataJob(config, resourceName) {
|
|
773
|
+
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
774
|
+
const customerId = this.getCleanCid_(config.customer_id);
|
|
775
|
+
const reportConfig = {
|
|
776
|
+
entity: 'offline_user_data_job',
|
|
777
|
+
attributes: [
|
|
778
|
+
'offline_user_data_job.id',
|
|
779
|
+
'offline_user_data_job.status',
|
|
780
|
+
'offline_user_data_job.type',
|
|
781
|
+
'offline_user_data_job.customer_match_user_list_metadata.user_list',
|
|
782
|
+
'offline_user_data_job.failure_reason',
|
|
783
|
+
],
|
|
784
|
+
constraints: {
|
|
785
|
+
'offline_user_data_job.resource_name': resourceName,
|
|
786
|
+
},
|
|
787
|
+
};
|
|
788
|
+
const jobs = await this.getReport(customerId, loginCustomerId, reportConfig);
|
|
789
|
+
if (jobs.length === 0) {
|
|
790
|
+
throw new Error(`Can't find the OfflineUserDataJob: ${resourceName}`);
|
|
791
|
+
}
|
|
792
|
+
return OfflineUserDataJobStatus[jobs[0].offline_user_data_job.status];
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
//resource_name: 'customers/8368692804/offlineUserDataJobs/23130531867'
|
|
796
|
+
//'customers/8368692804/offlineUserDataJobs/23232922761'
|
|
797
|
+
/**
|
|
798
|
+
* Creates a OfflineUserDataJob and returns resource name.
|
|
799
|
+
* @param {OfflineUserDataJobConfig} config Offline user data job config.
|
|
800
|
+
* @return {string} The resouce name of the creaed job.
|
|
801
|
+
*/
|
|
802
|
+
async createOfflineUserDataJob(config) {
|
|
803
|
+
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
804
|
+
const customerId = this.getCleanCid_(config.customer_id);
|
|
805
|
+
const { list_id: userListId, type } = config;
|
|
806
|
+
this.logger.debug('Creating OfflineUserDataJob for CID:', customerId);
|
|
807
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
808
|
+
// if()CUSTOMER_MATCH_USER_LIST
|
|
809
|
+
const job = OfflineUserDataJob.create({
|
|
810
|
+
type,
|
|
811
|
+
});
|
|
812
|
+
// https://developers.google.com/google-ads/api/rest/reference/rest/latest/customers.offlineUserDataJobs?hl=en#CustomerMatchUserListMetadata
|
|
813
|
+
if (type.startsWith('CUSTOMER_MATCH')) {
|
|
814
|
+
const metadata = this.buildCustomerMatchUserListMetadata_(customerId,
|
|
815
|
+
userListId);
|
|
816
|
+
job.customer_match_user_list_metadata = metadata;
|
|
817
|
+
// https://developers.google.com/google-ads/api/rest/reference/rest/latest/customers.offlineUserDataJobs?hl=en#StoreSalesMetadata
|
|
818
|
+
} else if (type.startsWith('STORE_SALES')) {
|
|
819
|
+
// If there is StoreSalesMetadata in the config
|
|
820
|
+
if (config.storeSalesMetadata) {
|
|
821
|
+
job.store_sales_list_metadata = config.storeSalesMetadata;
|
|
822
|
+
}
|
|
823
|
+
} else {
|
|
824
|
+
throw new Error(`UNSUPPORTED OfflineUserDataJobType: ${type}.`);
|
|
825
|
+
}
|
|
826
|
+
const request = CreateOfflineUserDataJobRequest.create({
|
|
827
|
+
customer_id: customerId,
|
|
828
|
+
job,
|
|
829
|
+
validate_only: this.debugMode, // when true makes no changes
|
|
830
|
+
enable_match_rate_range_preview: true,
|
|
831
|
+
});
|
|
832
|
+
try {
|
|
833
|
+
const { resource_name: resourceName } =
|
|
834
|
+
await customer.offlineUserDataJobs.createOfflineUserDataJob(request);
|
|
835
|
+
this.logger.info('Created OfflineUserDataJob:', resourceName);
|
|
836
|
+
return resourceName;
|
|
837
|
+
} catch (error) {
|
|
838
|
+
this.logger.error(error);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Adds user data in to the OfflineUserDataJob.
|
|
844
|
+
* @param {OfflineUserDataJobConfig} config Offline user data job config.
|
|
845
|
+
* @param {string} jobResourceName
|
|
846
|
+
* @param {!Array<CustomerMatchRecord>} customerMatchRecords user Ids
|
|
847
|
+
* @return {!Promise<AddOfflineUserDataJobOperationsResponse>}
|
|
848
|
+
*/
|
|
849
|
+
async addOperationsToOfflineUserDataJob(config, jobResourceName, records) {
|
|
850
|
+
const start = new Date().getTime();
|
|
851
|
+
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
852
|
+
const customerId = this.getCleanCid_(config.customer_id);
|
|
853
|
+
const operation = config.operation;
|
|
854
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
855
|
+
const operationsList = this.buildOperationsList_(operation, records);
|
|
856
|
+
const request = AddOfflineUserDataJobOperationsRequest.create({
|
|
857
|
+
resource_name: jobResourceName,
|
|
858
|
+
operations: operationsList,
|
|
859
|
+
validate_only: false,//this.debugMode,
|
|
860
|
+
enable_partial_failure: true,
|
|
861
|
+
enable_warnings: true,
|
|
862
|
+
});
|
|
863
|
+
// console.log(request);
|
|
864
|
+
const response = await customer.
|
|
865
|
+
offlineUserDataJobs.addOfflineUserDataJobOperations(request);
|
|
866
|
+
// console.log(response);
|
|
867
|
+
console.log('Request size:', JSON.stringify(request).length);
|
|
868
|
+
console.log('Time used:', new Date().getTime() - start);
|
|
869
|
+
return response;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* Starts the OfflineUserDataJob.
|
|
874
|
+
* @param {OfflineUserDataJobConfig} config Offline user data job config.
|
|
875
|
+
* @param {string} jobResourceName
|
|
876
|
+
* @returns
|
|
877
|
+
*/
|
|
878
|
+
async runOfflineUserDataJob(config, jobResourceName) {
|
|
879
|
+
const loginCustomerId = this.getCleanCid_(config.login_customer_id);
|
|
880
|
+
const customerId = this.getCleanCid_(config.customer_id);
|
|
881
|
+
const customer = this.getGoogleAdsApiCustomer_(loginCustomerId, customerId);
|
|
882
|
+
const request = RunOfflineUserDataJobRequest.create({
|
|
883
|
+
resource_name: jobResourceName,
|
|
884
|
+
validate_only: false,//this.debugMode,
|
|
885
|
+
});
|
|
886
|
+
try {
|
|
887
|
+
const rawResponse = await customer.
|
|
888
|
+
offlineUserDataJobs.runOfflineUserDataJob(request);
|
|
889
|
+
const response = lodash.pick(rawResponse, ['name', 'done', 'error']);
|
|
890
|
+
console.log(response);
|
|
891
|
+
return response;
|
|
892
|
+
} catch (e) {
|
|
893
|
+
console.log(e);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Returns the function to send out a request to Google Ads API with
|
|
899
|
+
* user data as operations in OfflineUserDataJob.
|
|
900
|
+
* @param {!OfflineUserDataJobConfig} config
|
|
901
|
+
* @param {string} jobResourceName
|
|
902
|
+
* @return {!SendSingleBatch} Function which can send a batch of hits to
|
|
903
|
+
* Google Ads API.
|
|
904
|
+
*/
|
|
905
|
+
getAddOperationsToOfflineUserDataJobFn(config, jobResourceName) {
|
|
906
|
+
/**
|
|
907
|
+
* Sends a batch of hits to Google Ads API.
|
|
908
|
+
* @param {!Array<string>} lines Data for single request. It should be
|
|
909
|
+
* guaranteed that it doesn't exceed quota limitation.
|
|
910
|
+
* @param {string} batchId The tag for log.
|
|
911
|
+
* @return {!Promise<BatchResult>}
|
|
912
|
+
*/
|
|
913
|
+
return async (lines, batchId) => {
|
|
914
|
+
/** @type {Array<CustomerMatchRecord>} */
|
|
915
|
+
const records = lines.map((line) => JSON.parse(line));
|
|
916
|
+
/** @const {BatchResult} */ const batchResult = {
|
|
917
|
+
result: true,
|
|
918
|
+
numberOfLines: lines.length,
|
|
919
|
+
};
|
|
920
|
+
try {
|
|
921
|
+
const response = await this.addOperationsToOfflineUserDataJob(config,
|
|
922
|
+
jobResourceName, records);
|
|
923
|
+
this.logger.debug(`Add operation to job batch[${batchId}]`, response);
|
|
924
|
+
const { results, partial_failure_error: failed } = response;
|
|
925
|
+
if (failed) {
|
|
926
|
+
this.logger.info('partial_failure_error:', failed.message);
|
|
927
|
+
const failures = failed.details.map(
|
|
928
|
+
({ value }) => GoogleAdsFailure.decode(value));
|
|
929
|
+
this.extraFailedLines_(batchResult, failures, lines, 0);
|
|
930
|
+
}
|
|
931
|
+
return batchResult;
|
|
932
|
+
} catch (error) {
|
|
933
|
+
this.logger.error(
|
|
934
|
+
`Error in OfflineUserDataJob add operations batch[${batchId}]`, error);
|
|
935
|
+
this.updateBatchResultWithError_(batchResult, error, lines, 2);
|
|
936
|
+
return batchResult;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
691
941
|
/**
|
|
692
942
|
* Returns an instance of GoogleAdsApi.Customer on google-ads-api.
|
|
693
943
|
* @param {string} loginCustomerId Login customer account ID (Mcc Account id).
|
|
@@ -748,6 +998,8 @@ module.exports = {
|
|
|
748
998
|
ConversionConfig,
|
|
749
999
|
CustomerMatchRecord,
|
|
750
1000
|
CustomerMatchConfig,
|
|
1001
|
+
OfflineUserDataJobType,
|
|
1002
|
+
OfflineUserDataJobConfig,
|
|
751
1003
|
GoogleAds,
|
|
752
1004
|
ReportQueryConfig,
|
|
753
1005
|
GoogleAdsField,
|