@google-cloud/nodejs-common 2.0.3-alpha → 2.0.5-alpha

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": "2.0.3-alpha",
3
+ "version": "2.0.5-alpha",
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,23 +16,23 @@
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": "^3.0.0",
20
- "@google-cloud/automl": "^4.0.0",
21
- "@google-cloud/bigquery": "^7.2.0",
22
- "@google-cloud/datastore": "^8.2.2",
23
- "@google-cloud/firestore": "^6.7.0",
19
+ "@google-cloud/aiplatform": "^3.10.0",
20
+ "@google-cloud/automl": "^4.0.1",
21
+ "@google-cloud/bigquery": "^7.3.0",
22
+ "@google-cloud/datastore": "^8.4.0",
23
+ "@google-cloud/firestore": "^7.2.0",
24
24
  "@google-cloud/logging-winston": "^6.0.0",
25
- "@google-cloud/pubsub": "^4.0.2",
26
- "@google-cloud/storage": "^7.0.1",
27
- "@google-cloud/scheduler": "^4.0.0",
28
- "@google-cloud/secret-manager": "^5.0.0",
29
- "gaxios": "^6.1.0",
25
+ "@google-cloud/pubsub": "^4.1.1",
26
+ "@google-cloud/storage": "^7.7.0",
27
+ "@google-cloud/scheduler": "^4.0.1",
28
+ "@google-cloud/secret-manager": "^5.0.1",
29
+ "gaxios": "^6.1.1",
30
30
  "google-ads-api": "^14.1.0",
31
31
  "google-ads-node": "^12.0.2",
32
- "google-auth-library": "^9.0.0",
33
- "googleapis": "^126.0.1",
32
+ "google-auth-library": "^9.4.2",
33
+ "googleapis": "^131.0.0",
34
34
  "winston": "^3.10.0",
35
- "@grpc/grpc-js": "1.9.5",
35
+ "@grpc/grpc-js": "1.9.14",
36
36
  "lodash": "^4.17.21"
37
37
  },
38
38
  "devDependencies": {
@@ -41,12 +41,16 @@ const API_VERSION = 'v4';
41
41
  * profileId, idType, conversion, customVariables, encryptionInfo.
42
42
  * The 'idType' can be one of the values: 'encryptedUserId', 'gclid' or
43
43
  * 'mobileDeviceId'.
44
+ * The 'operation' can be 'insert' or 'update'. The default value is 'insert'.
45
+ * 'insert' stands for https://developers.google.com/doubleclick-advertisers/rest/v4/conversions/batchinsert
46
+ * 'update' stands for https://developers.google.com/doubleclick-advertisers/rest/v4/conversions/batchupdate
44
47
  * For other properties, see
45
48
  * https://developers.google.com/doubleclick-advertisers/guides/conversions_update
46
49
  *
47
50
  * @typedef {{
48
51
  * profileId:string,
49
52
  * idType:string,
53
+ * operation:'insert'|'update'|undefined,
50
54
  * conversion:{
51
55
  * floodlightConfigurationId:string,
52
56
  * floodlightActivityId:string,
@@ -60,7 +64,7 @@ const API_VERSION = 'v4';
60
64
  * }|undefined),
61
65
  * }}
62
66
  */
63
- let InsertConversionsConfig;
67
+ let ConversionsConfig;
64
68
 
65
69
  /**
66
70
  * List of properties that will be take from the data file as elements of a
@@ -155,7 +159,7 @@ class DfaReporting {
155
159
  /**
156
160
  * Returns the function to sends out a request to CM with a batch of
157
161
  * conversions.
158
- * @param {!InsertConversionsConfig} config Campaign Manager configuration.
162
+ * @param {!ConversionsConfig} config Campaign Manager configuration.
159
163
  * @return {!SendSingleBatch} Function which can send a batch of hits to
160
164
  * Campaign Manager.
161
165
  */
@@ -171,6 +175,15 @@ class DfaReporting {
171
175
  /** @type {function} Gets the conversion elements from the data object. */
172
176
  const filterObject = getFilterFunction(PICKED_PROPERTIES);
173
177
  const time = new Date().getTime();
178
+ const operation =
179
+ config.operation === 'update' ? 'batchupdate' : 'batchinsert';
180
+ // customVariables is not supported by batchupdate.
181
+ // https://developers.google.com/doubleclick-advertisers/rest/v4/Conversion#CustomFloodlightVariable
182
+ if (operation === 'batchupdate' &&
183
+ typeof config.customVariables !== 'undefined') {
184
+ this.logger.warn('customVariables is not supported by batchupdate');
185
+ delete config.customVariables;
186
+ }
174
187
  const conversions = lines.map((line) => {
175
188
  const record = JSON.parse(line);
176
189
  const conversion = Object.assign(
@@ -184,7 +197,7 @@ class DfaReporting {
184
197
  // Custom Variables
185
198
  if (typeof config.customVariables !== 'undefined') {
186
199
  conversion.customVariables = config.customVariables.map(
187
- (variable) => ({'type': variable, 'value': record[variable],}));
200
+ (variable) => ({ 'type': variable, 'value': record[variable], }));
188
201
  }
189
202
  // User Identifiers
190
203
  if (record.userIdentifiers) {
@@ -225,7 +238,7 @@ class DfaReporting {
225
238
  };
226
239
  try {
227
240
  const dfareporting = await this.getApiClient_();
228
- const response = await dfareporting.conversions.batchinsert({
241
+ const response = await dfareporting.conversions[operation]({
229
242
  profileId: config.profileId,
230
243
  requestBody: requestBody,
231
244
  });
@@ -411,7 +424,7 @@ class DfaReporting {
411
424
 
412
425
  module.exports = {
413
426
  DfaReporting,
414
- InsertConversionsConfig,
427
+ ConversionsConfig,
415
428
  API_VERSION,
416
429
  API_SCOPES,
417
430
  };
@@ -31,7 +31,7 @@ const {
31
31
  const API_SCOPES = Object.freeze([
32
32
  'https://www.googleapis.com/auth/display-video',
33
33
  ]);
34
- const API_VERSION = 'v2';
34
+ const API_VERSION = 'v3';
35
35
 
36
36
  /**
37
37
  * Display and Video 360 API v2 stub.
@@ -370,7 +370,9 @@ class DoubleClickSearch {
370
370
  const doubleclicksearch = await this.getApiClient_();
371
371
  const response = await doubleclicksearch.reports.getFile(
372
372
  {reportId, reportFragment});
373
- if (response.status === 200) return response.data;
373
+ if (response.status === 200) {
374
+ return Buffer.from(await response.data.arrayBuffer()).toString();
375
+ }
374
376
  const errorMsg =
375
377
  `Error in get file from reports: ${reportFragment}@${reportId}`;
376
378
  this.logger.error(errorMsg, response);
package/src/apis/index.js CHANGED
@@ -25,7 +25,7 @@ exports.AuthClient = require('./auth_client.js');
25
25
  * APIs integration class for DFA Reporting API.
26
26
  * @const {{
27
27
  * DfaReporting:!DfaReporting,
28
- * InsertConversionsConfig:!InsertConversionsConfig,
28
+ * ConversionsConfig:!ConversionsConfig,
29
29
  * }}
30
30
  */
31
31
  exports.dfareporting = require('./dfa_reporting.js');
@@ -77,6 +77,14 @@ exports.spreadsheets = require('./spreadsheets.js');
77
77
  */
78
78
  exports.doubleclicksearch = require('./doubleclick_search.js');
79
79
 
80
+ /**
81
+ * APIs integration class for Search Ads 360 Reporting.
82
+ * @const {{
83
+ * SearchAds:!SearchAds,
84
+ * }}
85
+ */
86
+ exports.searchads = require('./search_ads.js');
87
+
80
88
  /**
81
89
  * APIs integration class for DoubleClick BidManager (DV360).
82
90
  * @const {{
@@ -0,0 +1,171 @@
1
+ // Copyright 2019 Google Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ /**
16
+ * @fileoverview Google Search Ads 360 Reporting on GoogleAPI Client Library.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ const { google } = require('googleapis');
22
+ const AuthClient = require('./auth_client.js');
23
+ const { getLogger } = require('../components/utils.js');
24
+
25
+ const API_SCOPES = Object.freeze([
26
+ 'https://www.googleapis.com/auth/doubleclicksearch',
27
+ ]);
28
+ const API_VERSION = 'v0';
29
+
30
+ /**
31
+ * Search Ads 360 Reporting API stub.
32
+ * See: https://developers.google.com/search-ads/reporting/api/reference/release-notes
33
+ */
34
+ class SearchAds {
35
+
36
+ /**
37
+ * @constructor
38
+ * @param {!Object<string,string>=} env The environment object to hold env
39
+ * variables.
40
+ */
41
+ constructor(env = process.env) {
42
+ this.authClient = new AuthClient(API_SCOPES, env);
43
+ this.logger = getLogger('API.SA');
44
+ }
45
+
46
+ /**
47
+ * Prepares the Search Ads 360 Reporting API instance.
48
+ * OAuth 2.0 application credentials is required for calling this API.
49
+ * For Search Ads Reporting API calls made by a manager to a client account,
50
+ * a HTTP header named `login-customer-id` is required in the request. This
51
+ * value represents the Search Ads 360 customer ID of the manager making the
52
+ * API call. Be sure to remove any hyphens (—), for example: 1234567890, not
53
+ * 123-456-7890.
54
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/auth
55
+ * @return {!google.searchads360}
56
+ * @private
57
+ */
58
+ async getApiClient_(loginCustomerId) {
59
+ this.logger.debug(`Initialized Search Ads reporting instance for ${loginCustomerId}`);
60
+ this.searchads360 = google.searchads360({
61
+ version: API_VERSION,
62
+ auth: await this.getAuth_(),
63
+ headers: { 'login-customer-id': loginCustomerId },
64
+ });
65
+ return this.searchads360;
66
+ }
67
+
68
+ /**
69
+ * Gets the auth object.
70
+ * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
71
+ */
72
+ async getAuth_() {
73
+ if (this.auth) return this.auth;
74
+ await this.authClient.prepareCredentials();
75
+ this.auth = this.authClient.getDefaultAuth();
76
+ return this.auth;
77
+ }
78
+
79
+ /**
80
+ * Returns all rows that match the search stream query.
81
+ * The streamed content is not NDJSON format, but one JSON object with the
82
+ * property `results`. The whole JSON string can be parsed to an object and
83
+ * the `results` be extracted and converted to NDJSON lines. If the report
84
+ * is too large to be handled in this way, a possible solution is to parse
85
+ * the string directly to get the content of `results`.
86
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.searchAds360/searchStream
87
+ * @param {string} customerId
88
+ * @param {string} loginCustomerId Login customer account ID (Mcc Account id).
89
+ * @param {string} query
90
+ * @return {!ReadableStream}
91
+ */
92
+ async streamReport(customerId, loginCustomerId, query) {
93
+ const searchads = await this.getApiClient_(loginCustomerId);
94
+ const response = await searchads.customers.searchAds360.search({
95
+ customerId,
96
+ requestBody: { query },
97
+ }, { responseType: 'stream' });
98
+ return response.data;
99
+ }
100
+
101
+ /**
102
+ * Gets a report synchronously from a given Customer account.
103
+ * This is for test as it does not handle page token. For product env, use
104
+ * function `streamReport`.
105
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.searchAds360/search#request-body
106
+ * @param {string} customerId
107
+ * @param {string} loginCustomerId Login customer account ID (Mcc Account id).
108
+ * @param {string} query
109
+ * @return {!SearchAds360Field}
110
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields#SearchAds360Field
111
+ */
112
+ async getReport(customerId, loginCustomerId, query) {
113
+ const searchads = await this.getApiClient_(loginCustomerId);
114
+ const response = await searchads.customers.searchAds360.search({
115
+ customerId,
116
+ requestBody: { query },
117
+ });
118
+ return response.data.results;
119
+ }
120
+
121
+ /**
122
+ * Returns the requested field or resource (artifact) used by SearchAds360Service.
123
+ * This service doesn't require `login-customer-id` HTTP header.
124
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields/get
125
+ * @param {string} resourceName
126
+ * @return {!SearchAds360Field}
127
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields#SearchAds360Field
128
+ */
129
+ async getReportField(resourceName) {
130
+ const searchads = await this.getApiClient_();
131
+ const response = await searchads.searchAds360Fields.get({ resourceName });
132
+ return response.data;
133
+ }
134
+
135
+ /**
136
+ * Returns all the custom columns associated with the customer in full detail.
137
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns/list
138
+ * @param {string} customerId - The ID of the customer.
139
+ * @param {string} loginCustomerId - The ID of the manager.
140
+ * @return {!Array<CustomColumn>}
141
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns#CustomColumn
142
+ */
143
+ async listCustomColumns(customerId, loginCustomerId) {
144
+ const searchads = await this.getApiClient_(loginCustomerId);
145
+ const response = await searchads.customers.customColumns.list({ customerId });
146
+ return response.data.customColumns;
147
+ }
148
+
149
+ /**
150
+ * Returns the requested custom column in full detail.
151
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns/get
152
+ * @param {string} columnId - The ID of the customColumn.
153
+ * @param {string} customerId - The ID of the customer.
154
+ * @param {string} loginCustomerId - The ID of the manager.
155
+ * @return {!CustomColumn}
156
+ * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns#CustomColumn
157
+ */
158
+ async getCustomColumn(columnId, customerId, loginCustomerId) {
159
+ const resourceName = `customers/${customerId}/customColumns/${columnId}`;
160
+ const searchads = await this.getApiClient_(loginCustomerId);
161
+ const response = await searchads.customers.customColumns.get({ resourceName });
162
+ return response.data;
163
+ }
164
+
165
+ }
166
+
167
+ module.exports = {
168
+ SearchAds,
169
+ API_VERSION,
170
+ API_SCOPES,
171
+ };
@@ -229,4 +229,3 @@ module.exports = {
229
229
  getIdTokenForFunction,
230
230
  convertEnvPathToAbsolute,
231
231
  };
232
-
@@ -179,7 +179,7 @@ class StorageFile {
179
179
  .on('finish', async () => {
180
180
  const [{ contentType }] = await this.file.getMetadata();
181
181
  const [file] = await outputFile.setMetadata({ contentType });
182
- return file.name;
182
+ resolve(file.name);
183
183
  });
184
184
  });
185
185
  }
@@ -255,4 +255,3 @@ module.exports = {
255
255
  LINE_BREAKER,
256
256
  DEFAULT_SPLIT_SIZE,
257
257
  };
258
-
@@ -21,6 +21,8 @@
21
21
  const winston = require('winston');
22
22
  const {inspect} = require('util');
23
23
  const { LoggingWinston } = require('@google-cloud/logging-winston');
24
+ const { request } = require('gaxios');
25
+ const lodash = require('lodash');
24
26
 
25
27
  /**
26
28
  * The result of a batch of data sent to target API. The batch here means the
@@ -537,6 +539,34 @@ const changeNamingFromSnakeToLowerCamel = (name) => {
537
539
  (initial) => initial.substring(1).toUpperCase());
538
540
  };
539
541
 
542
+ /**
543
+ * Returns the response data for a HTTP request. It will retry the specific
544
+ * times if there was errors happened.
545
+ * @param {object} options Options for the request.
546
+ * @param {object=} logger Default is `console`.
547
+ * @param {number=} retryTimes Default value 3.
548
+ * @return {object}
549
+ */
550
+ const requestWithRetry = async (options, logger = console, retryTimes = 3) => {
551
+ let processedTimes = 0;
552
+ do {
553
+ // Wait sometime (2s, 4s, 8s, ...) before each retry.
554
+ if (processedTimes > 0) await wait(2 ** processedTimes * 1000);
555
+ try {
556
+ const requestOption = lodash.merge({
557
+ responseType: 'json',
558
+ method: 'POST',
559
+ }, options);
560
+ const response = await request(requestOption);
561
+ return response.data;
562
+ } catch (error) {
563
+ processedTimes++;
564
+ if (processedTimes > retryTimes) throw error;
565
+ logger.error(`Request ${JSON.stringify(options)}`, error);
566
+ }
567
+ } while (processedTimes <= retryTimes)
568
+ }
569
+
540
570
  // noinspection JSUnusedAssignment
541
571
  module.exports = {
542
572
  getLogger,
@@ -554,4 +584,5 @@ module.exports = {
554
584
  getObjectByPath,
555
585
  changeNamingFromSnakeToUpperCamel,
556
586
  changeNamingFromSnakeToLowerCamel,
587
+ requestWithRetry,
557
588
  };