@google-cloud/nodejs-common 1.5.8-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.8-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",
@@ -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 },
@@ -107,6 +111,16 @@ const IDENTIFIERS = [
107
111
  'address_info',
108
112
  ];
109
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
+
110
124
  /**
111
125
  * Maximum number of user identifiers in single UserData.
112
126
  * @see https://ads-developers.googleblog.com/2021/10/userdata-enforcement-in-google-ads-api.html
@@ -181,11 +195,17 @@ let CustomerMatchConfig;
181
195
  * Configuration for offline user data job, includes:
182
196
  * customer_id, login_customer_id, list_id, operation and type.
183
197
  * 'operation' should be one of the two: 'create' or 'remove',
184
- * 'type' is OfflineUserDataJobType, it can be 'CUSTOMER_MATCH_USER_LIST' or
185
- * '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'.
186
200
  * For job type 'CUSTOMER_MATCH_USER_LIST', if `list_id` is not present,
187
201
  * 'list_name' and 'upload_key_type' need to be there so they can be used to
188
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.
189
209
  * @see https://developers.google.com/google-ads/api/reference/rpc/latest/OfflineUserDataJob
190
210
  * @typedef {{
191
211
  * customer_id: (string|number),
@@ -195,7 +215,9 @@ let CustomerMatchConfig;
195
215
  * upload_key_type: ('CONTACT_INFO'|'CRM_ID'|'MOBILE_ADVERTISING_ID'|undefined),
196
216
  * operation: ('create'|'remove'),
197
217
  * type: !OfflineUserDataJobType,
198
- * storeSalesMetadata: (undefined|object),
218
+ * store_sales_metadata: (undefined|StoreSalesMetadata),
219
+ * transaction_attribute: (undefined|TransactionAttribute),
220
+ * user_attribute: (undefined|UserAttribute),
199
221
  * }}
200
222
  */
201
223
  let OfflineUserDataJobConfig;
@@ -804,10 +826,16 @@ class GoogleAds {
804
826
  * @see https://developers.google.com/google-ads/api/reference/rpc/latest/UserDataOperation
805
827
  * @param {string} operationType either 'create' or 'remove'
806
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'.
807
834
  * @return {Array<UserDataOperation>}
808
835
  * @private
809
836
  */
810
- buildOperationsList_(operationType, customerMatchRecords) {
837
+ buildOperationsList_(operationType, customerMatchRecords,
838
+ additionalAttributes = {}) {
811
839
  return customerMatchRecords.map((customerMatchRecord) => {
812
840
  const userIdentifiers = [];
813
841
  IDENTIFIERS.forEach((idType) => {
@@ -822,17 +850,24 @@ class GoogleAds {
822
850
  }
823
851
  }
824
852
  });
825
- let userData;
853
+ const userData = {};
826
854
  if (userIdentifiers.length <= MAX_IDENTIFIERS_PER_USER) {
827
- userData = UserData.create({user_identifiers: userIdentifiers});
855
+ userData.user_identifiers = userIdentifiers;
828
856
  } else {
829
857
  this.logger.warn(
830
858
  `Too many user identifiers, will only send ${MAX_IDENTIFIERS_PER_USER}:`,
831
859
  JSON.stringify(customerMatchRecord));
832
- userData = UserData.create({user_identifiers: userIdentifiers}.slice(0,
833
- MAX_IDENTIFIERS_PER_USER));
860
+ userData.user_identifiers =
861
+ userIdentifiers.slice(0, MAX_IDENTIFIERS_PER_USER);
834
862
  }
835
- 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) });
836
871
  });
837
872
  }
838
873
 
@@ -887,7 +922,12 @@ class GoogleAds {
887
922
  if (jobs.length === 0) {
888
923
  throw new Error(`Can't find the OfflineUserDataJob: ${resourceName}`);
889
924
  }
890
- 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];
891
931
  }
892
932
 
893
933
  /**
@@ -912,9 +952,10 @@ class GoogleAds {
912
952
  job.customer_match_user_list_metadata = metadata;
913
953
  // https://developers.google.com/google-ads/api/rest/reference/rest/latest/customers.offlineUserDataJobs?hl=en#StoreSalesMetadata
914
954
  } else if (type.startsWith('STORE_SALES')) {
915
- // If there is StoreSalesMetadata in the config
916
- if (config.storeSalesMetadata) {
917
- 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;
918
959
  }
919
960
  } else {
920
961
  throw new Error(`UNSUPPORTED OfflineUserDataJobType: ${type}.`);
@@ -945,7 +986,7 @@ class GoogleAds {
945
986
  const operation = config.operation;
946
987
  const customer = await this.getGoogleAdsApiCustomer_(
947
988
  loginCustomerId, customerId);
948
- const operationsList = this.buildOperationsList_(operation, records);
989
+ const operationsList = this.buildOperationsList_(operation, records, config);
949
990
  const request = AddOfflineUserDataJobOperationsRequest.create({
950
991
  resource_name: jobResourceName,
951
992
  operations: operationsList,