@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.
@@ -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.0",
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": "^10.0.1",
30
- "google-ads-node":"^8.0.1",
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",
@@ -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 adjustments for
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
- getUploadConversionFn(customerId, loginCustomerId, adsConfig) {
275
- /**
276
- * Sends a batch of hits to Google Ads API.
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
- (line) => buildClickConversionFromLine(line, adsConfig, customerId));
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.uploadConversionAdjustments(conversions,
344
- customerId, loginCustomerId);
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 orderId = results.map((conversion) => conversion.order_id);
348
- this.logger.debug('Uploaded order_id:', orderId);
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
- ({value}) => GoogleAdsFailure.decode(value));
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
- `Error in upload conversion adjustments batch: ${batchId}`, error);
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.replace(/-/g, '');
617
- const loginCustomerId = customerMatchConfig.login_customer_id.replace(/-/g,
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,