@google-cloud/nodejs-common 1.5.7-beta → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@google-cloud/nodejs-common",
3
- "version": "1.5.7-beta",
3
+ "version": "1.6.0",
4
4
  "description": "A NodeJs common library for solutions based on Cloud Functions",
5
5
  "author": "Google Inc.",
6
6
  "license": "Apache-2.0",
@@ -16,21 +16,21 @@
16
16
  },
17
17
  "homepage": "https://github.com/GoogleCloudPlatform/cloud-for-marketing/blob/master/marketing-analytics/activation/common-libs/nodejs-common/README.md",
18
18
  "dependencies": {
19
- "@google-cloud/aiplatform": "^2.3.0",
20
- "@google-cloud/automl": "^3.1.0",
21
- "@google-cloud/bigquery": "^6.0.3",
22
- "@google-cloud/datastore": "^7.0.0",
23
- "@google-cloud/firestore": "^6.3.0",
24
- "@google-cloud/logging-winston": "^5.1.5",
25
- "@google-cloud/pubsub": "^3.2.0",
26
- "@google-cloud/storage": "^6.5.2",
27
- "@google-cloud/scheduler": "^3.0.4",
28
- "@google-cloud/secret-manager": "^4.1.3",
19
+ "@google-cloud/aiplatform": "^2.5.0",
20
+ "@google-cloud/automl": "^3.1.2",
21
+ "@google-cloud/bigquery": "^6.1.0",
22
+ "@google-cloud/datastore": "^7.1.0",
23
+ "@google-cloud/firestore": "^6.4.2",
24
+ "@google-cloud/logging-winston": "^5.3.0",
25
+ "@google-cloud/pubsub": "^3.3.0",
26
+ "@google-cloud/storage": "^6.9.2",
27
+ "@google-cloud/scheduler": "^3.2.0",
28
+ "@google-cloud/secret-manager": "^4.2.0",
29
29
  "gaxios": "^5.0.2",
30
- "google-ads-api": "^11.1.0",
31
- "google-ads-node": "^9.1.0",
32
- "google-auth-library": "^8.5.2",
33
- "googleapis": "^108.0.0",
30
+ "google-ads-api": "^12.0.1",
31
+ "google-ads-node": "^10.0.0",
32
+ "google-auth-library": "^8.7.0",
33
+ "googleapis": "^110.0.0",
34
34
  "winston": "^3.8.2",
35
35
  "lodash": "^4.17.21"
36
36
  },
@@ -284,6 +284,10 @@ class DfaReporting {
284
284
  * Runs a report and return the file Id. As an asynchronized process, the
285
285
  * returned file Id will be a placeholder until the status changes to
286
286
  * 'REPORT_AVAILABLE' in the response of `getFile`.
287
+ * Campaign Manager reports use a fixed timezone(America/Los Angeles) when it
288
+ * has a relative date range, e.g. YESTERDAY. To solve this, if there are
289
+ * `startDate` and `endDate` available in the given `config`, they will be
290
+ * used to update the report before the report is started to run.
287
291
  * @see https://developers.google.com/doubleclick-advertisers/rest/v4/reports/run
288
292
  *
289
293
  * @param {{
@@ -296,6 +300,24 @@ class DfaReporting {
296
300
  async runReport(config) {
297
301
  const profileId = await this.getProfileForOperation_(config);
298
302
  const dfareporting = await this.getApiClient_();
303
+ const { startDate, endDate } = config;
304
+ if (startDate && endDate) {
305
+ const { data: report } = await dfareporting.reports.get({
306
+ profileId,
307
+ reportId: config.reportId,
308
+ });
309
+ report.criteria.dateRange = { startDate, endDate };
310
+ const updated = await dfareporting.reports.update({
311
+ profileId,
312
+ reportId: config.reportId,
313
+ requestBody: report
314
+ });
315
+ if (updated.status >= 400) {
316
+ this.logger.error(
317
+ 'Failed to update data range of CM360 report', config.reportId);
318
+ this.logger.error('Report to be updated', report);
319
+ }
320
+ }
299
321
  const response = await dfareporting.reports.run({
300
322
  profileId,
301
323
  reportId: config.reportId,
@@ -21,9 +21,12 @@ const {protos: {google: {ads: {googleads}}}} = require('google-ads-node');
21
21
  const googleAdsLib = googleads[Object.keys(googleads)[0]];
22
22
  const {
23
23
  common: {
24
+ CustomerMatchUserListMetadata,
25
+ StoreSalesMetadata,
26
+ TransactionAttribute,
27
+ UserAttribute,
24
28
  UserData,
25
29
  UserIdentifier,
26
- CustomerMatchUserListMetadata,
27
30
  },
28
31
  resources: {
29
32
  GoogleAdsField,
@@ -31,24 +34,25 @@ const {
31
34
  UserList,
32
35
  },
33
36
  services: {
34
- CreateOfflineUserDataJobRequest,
35
37
  AddOfflineUserDataJobOperationsRequest,
38
+ CreateOfflineUserDataJobRequest,
36
39
  RunOfflineUserDataJobRequest,
40
+ SearchGoogleAdsFieldsRequest,
37
41
  UploadCallConversionsRequest,
38
- UploadClickConversionsRequest,
39
42
  UploadCallConversionsResponse,
43
+ UploadClickConversionsRequest,
40
44
  UploadClickConversionsResponse,
41
45
  UploadConversionAdjustmentsRequest,
42
46
  UploadConversionAdjustmentsResponse,
43
47
  UploadUserDataRequest,
44
48
  UploadUserDataResponse,
45
49
  UserDataOperation,
46
- SearchGoogleAdsFieldsRequest,
47
50
  },
48
51
  errors: {
49
52
  GoogleAdsFailure,
50
53
  },
51
54
  enums: {
55
+ OfflineUserDataJobFailureReasonEnum: { OfflineUserDataJobFailureReason },
52
56
  OfflineUserDataJobTypeEnum: { OfflineUserDataJobType },
53
57
  OfflineUserDataJobStatusEnum: { OfflineUserDataJobStatus },
54
58
  UserListMembershipStatusEnum: { UserListMembershipStatus },
@@ -77,6 +81,8 @@ const PICKED_PROPERTIES = [
77
81
  'external_attribution_data',
78
82
  'cart_data',
79
83
  'user_identifiers',
84
+ 'gbraid',
85
+ 'wbraid',
80
86
  'gclid',
81
87
  'caller_id',
82
88
  'call_start_date_time',
@@ -105,6 +111,16 @@ const IDENTIFIERS = [
105
111
  'address_info',
106
112
  ];
107
113
 
114
+ /**
115
+ * Additional attributes in user data for store sales data or customer match.
116
+ * @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserData
117
+ * @type {Array<string>}
118
+ */
119
+ const USERDATA_ADDITIONAL_ATTRIBUTES = [
120
+ 'transaction_attribute',
121
+ 'user_attribute',
122
+ ];
123
+
108
124
  /**
109
125
  * Maximum number of user identifiers in single UserData.
110
126
  * @see https://ads-developers.googleblog.com/2021/10/userdata-enforcement-in-google-ads-api.html
@@ -179,11 +195,17 @@ let CustomerMatchConfig;
179
195
  * Configuration for offline user data job, includes:
180
196
  * customer_id, login_customer_id, list_id, operation and type.
181
197
  * 'operation' should be one of the two: 'create' or 'remove',
182
- * 'type' is OfflineUserDataJobType, it can be 'CUSTOMER_MATCH_USER_LIST' or
183
- * 'STORE_SALES_UPLOAD_FIRST_PARTY'.
198
+ * 'type' is OfflineUserDataJobType, it can be 'CUSTOMER_MATCH_USER_LIST',
199
+ * 'CUSTOMER_MATCH_WITH_ATTRIBUTES' or 'STORE_SALES_UPLOAD_FIRST_PARTY'.
184
200
  * For job type 'CUSTOMER_MATCH_USER_LIST', if `list_id` is not present,
185
201
  * 'list_name' and 'upload_key_type' need to be there so they can be used to
186
202
  * create a customer match user list.
203
+ * For job type 'CUSTOMER_MATCH_WITH_ATTRIBUTES', 'user_attribute' can be used
204
+ * to store shared additional user attributes.
205
+ * For job type 'STORE_SALES_UPLOAD_FIRST_PARTY', `store_sales_metadata` is
206
+ * required to offer StoreSalesMetadata. Besides that, for the store sales data,
207
+ * common data (e.g. `currency_code`, `conversion_action`) in
208
+ * `transaction_attribute` can be put here as well.
187
209
  * @see https://developers.google.com/google-ads/api/reference/rpc/latest/OfflineUserDataJob
188
210
  * @typedef {{
189
211
  * customer_id: (string|number),
@@ -193,7 +215,9 @@ let CustomerMatchConfig;
193
215
  * upload_key_type: ('CONTACT_INFO'|'CRM_ID'|'MOBILE_ADVERTISING_ID'|undefined),
194
216
  * operation: ('create'|'remove'),
195
217
  * type: !OfflineUserDataJobType,
196
- * storeSalesMetadata: (undefined|object),
218
+ * store_sales_metadata: (undefined|StoreSalesMetadata),
219
+ * transaction_attribute: (undefined|TransactionAttribute),
220
+ * user_attribute: (undefined|UserAttribute),
197
221
  * }}
198
222
  */
199
223
  let OfflineUserDataJobConfig;
@@ -802,10 +826,16 @@ class GoogleAds {
802
826
  * @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserDataOperation
803
827
  * @param {string} operationType either 'create' or 'remove'
804
828
  * @param {Array<CustomerMatchRecord>} customerMatchRecords userIds
829
+ * @param {{
830
+ * transaction_attribute: TransactionAttribute|undefined,
831
+ * user_attribute: UserAttribute,
832
+ * }} additionalAttributes Additional attributes for 'UserData', includes
833
+ * 'transaction_attribute' or 'user_attribute'.
805
834
  * @return {Array<UserDataOperation>}
806
835
  * @private
807
836
  */
808
- buildOperationsList_(operationType, customerMatchRecords) {
837
+ buildOperationsList_(operationType, customerMatchRecords,
838
+ additionalAttributes = {}) {
809
839
  return customerMatchRecords.map((customerMatchRecord) => {
810
840
  const userIdentifiers = [];
811
841
  IDENTIFIERS.forEach((idType) => {
@@ -820,17 +850,24 @@ class GoogleAds {
820
850
  }
821
851
  }
822
852
  });
823
- let userData;
853
+ const userData = {};
824
854
  if (userIdentifiers.length <= MAX_IDENTIFIERS_PER_USER) {
825
- userData = UserData.create({user_identifiers: userIdentifiers});
855
+ userData.user_identifiers = userIdentifiers;
826
856
  } else {
827
857
  this.logger.warn(
828
858
  `Too many user identifiers, will only send ${MAX_IDENTIFIERS_PER_USER}:`,
829
859
  JSON.stringify(customerMatchRecord));
830
- userData = UserData.create({user_identifiers: userIdentifiers}.slice(0,
831
- MAX_IDENTIFIERS_PER_USER));
860
+ userData.user_identifiers =
861
+ userIdentifiers.slice(0, MAX_IDENTIFIERS_PER_USER);
832
862
  }
833
- return UserDataOperation.create({[operationType]: userData});
863
+ USERDATA_ADDITIONAL_ATTRIBUTES.forEach((attribute) => {
864
+ if (additionalAttributes[attribute] || customerMatchRecord[attribute]) {
865
+ userData[attribute] = lodash.merge(
866
+ {}, additionalAttributes[attribute], customerMatchRecord[attribute]);
867
+ }
868
+ })
869
+ return UserDataOperation.create(
870
+ { [operationType]: UserData.create(userData) });
834
871
  });
835
872
  }
836
873
 
@@ -885,7 +922,12 @@ class GoogleAds {
885
922
  if (jobs.length === 0) {
886
923
  throw new Error(`Can't find the OfflineUserDataJob: ${resourceName}`);
887
924
  }
888
- return OfflineUserDataJobStatus[jobs[0].offline_user_data_job.status];
925
+ const { failure_reason: failure, status } = jobs[0].offline_user_data_job;
926
+ if (failure > 0) {
927
+ this.logger.warn(`Offline UserData Job [${resourceName}] failed: `,
928
+ OfflineUserDataJobFailureReason[failure])
929
+ }
930
+ return OfflineUserDataJobStatus[status];
889
931
  }
890
932
 
891
933
  /**
@@ -910,9 +952,10 @@ class GoogleAds {
910
952
  job.customer_match_user_list_metadata = metadata;
911
953
  // https://developers.google.com/google-ads/api/rest/reference/rest/latest/customers.offlineUserDataJobs?hl=en#StoreSalesMetadata
912
954
  } else if (type.startsWith('STORE_SALES')) {
913
- // If there is StoreSalesMetadata in the config
914
- if (config.storeSalesMetadata) {
915
- job.store_sales_metadata = config.storeSalesMetadata;
955
+ // Support previous property 'StoreSalesMetadata' for compatibility.
956
+ if (config.store_sales_metadata || config.StoreSalesMetadata) {
957
+ job.store_sales_metadata = config.store_sales_metadata
958
+ || config.StoreSalesMetadata;
916
959
  }
917
960
  } else {
918
961
  throw new Error(`UNSUPPORTED OfflineUserDataJobType: ${type}.`);
@@ -943,7 +986,7 @@ class GoogleAds {
943
986
  const operation = config.operation;
944
987
  const customer = await this.getGoogleAdsApiCustomer_(
945
988
  loginCustomerId, customerId);
946
- const operationsList = this.buildOperationsList_(operation, records);
989
+ const operationsList = this.buildOperationsList_(operation, records, config);
947
990
  const request = AddOfflineUserDataJobOperationsRequest.create({
948
991
  resource_name: jobResourceName,
949
992
  operations: operationsList,
@@ -115,6 +115,7 @@ class DatastoreModeAccess {
115
115
  .save({
116
116
  key: key,
117
117
  data: data,
118
+ excludeLargeProperties: true,
118
119
  })
119
120
  .then((apiResponse) => {
120
121
  // Default key in Datastore is a number in response like following.