@google-cloud/nodejs-common 2.1.0 → 2.1.1

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.
@@ -1560,6 +1560,7 @@ set_cloud_functions_default_settings() {
1560
1560
  default_cf_flag+=(--set-env-vars=DEBUG="${DEBUG}")
1561
1561
  default_cf_flag+=(--set-env-vars=IN_GCP="${IN_GCP}")
1562
1562
  default_cf_flag+=(--set-env-vars=DATABASE_ID="${DATABASE_ID}")
1563
+ default_cf_flag+=(--set-env-vars=DATABASE_MODE="${DATABASE_MODE}")
1563
1564
  }
1564
1565
 
1565
1566
  #######################################
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@google-cloud/nodejs-common",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "A NodeJs common library for solutions based on Cloud Functions",
5
5
  "author": "Google Inc.",
6
6
  "license": "Apache-2.0",
@@ -21,6 +21,7 @@
21
21
 
22
22
  const stream = require('stream');
23
23
  const {google} = require('googleapis');
24
+ const { GoogleApiClient } = require('./base/google_api_client.js');
24
25
  const {Schema$Upload} = google.analytics;
25
26
  const AuthClient = require('./auth_client.js');
26
27
  const {wait, getLogger, BatchResult} = require('../components/utils.js');
@@ -55,31 +56,26 @@ let DataImportClearConfig;
55
56
  /**
56
57
  * Google Analytics API v3 stub.
57
58
  */
58
- class Analytics {
59
+ class Analytics extends GoogleApiClient {
59
60
  /**
60
61
  * @constructor
61
62
  * @param {!Object<string,string>=} env The environment object to hold env
62
63
  * variables.
63
64
  */
64
65
  constructor(env = process.env) {
65
- this.authClient = new AuthClient(API_SCOPES, env);
66
+ super(env);
67
+ this.googleApi = 'analytics';
66
68
  this.logger = getLogger('API.GA');
67
69
  }
68
70
 
69
- /**
70
- * Prepares the Google Analytics instance.
71
- * @return {!google.analytics}
72
- * @private
73
- */
74
- async getApiClient_() {
75
- if (this.analytics) return this.analytics;
76
- await this.authClient.prepareCredentials();
77
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
78
- this.analytics = google.analytics({
79
- version: API_VERSION,
80
- auth: this.authClient.getDefaultAuth(),
81
- });
82
- return this.analytics;
71
+ /** @override */
72
+ getScope() {
73
+ return API_SCOPES;
74
+ }
75
+
76
+ /** @override */
77
+ getVersion() {
78
+ return API_VERSION;
83
79
  }
84
80
 
85
81
  /**
@@ -102,7 +98,7 @@ class Analytics {
102
98
  },
103
99
  config);
104
100
 
105
- const analytics = await this.getApiClient_();
101
+ const analytics = await this.getApiClient();
106
102
  const response = await analytics.management.uploads.uploadData(
107
103
  uploadConfig);
108
104
  this.logger.debug('Configuration: ', config);
@@ -151,7 +147,7 @@ class Analytics {
151
147
  * @return {!Promise<!Schema$Upload>} Updated data import Job status.
152
148
  */
153
149
  async checkJobStatus(jobConfig) {
154
- const analytics = await this.getApiClient_();
150
+ const analytics = await this.getApiClient();
155
151
  const { data: job } = await analytics.management.uploads.get(jobConfig);
156
152
  if (job.status !== 'PENDING') return job;
157
153
  this.logger.debug(
@@ -169,7 +165,7 @@ class Analytics {
169
165
  * @return {!Promise<!Array<string>>}
170
166
  */
171
167
  async listAccounts() {
172
- const analytics = await this.getApiClient_();
168
+ const analytics = await this.getApiClient();
173
169
  const response = await analytics.management.accounts.list();
174
170
  return response.data.items.map(
175
171
  (account) => `Account id: ${account.name}[${account.id}]`
@@ -182,7 +178,7 @@ class Analytics {
182
178
  * @return {!Promise<!Array<Object>>}
183
179
  */
184
180
  async listUploads(config) {
185
- const analytics = await this.getApiClient_();
181
+ const analytics = await this.getApiClient();
186
182
  const response = await analytics.management.uploads.list(config);
187
183
  return response.data.items;
188
184
  }
@@ -203,7 +199,7 @@ class Analytics {
203
199
  const request = Object.assign({}, config, {
204
200
  resource: {customDataImportUids},
205
201
  });
206
- const analytics = await this.getApiClient_();
202
+ const analytics = await this.getApiClient();
207
203
  await analytics.management.uploads.deleteUploadData(request);
208
204
  this.logger.debug('Delete uploads: ', customDataImportUids);
209
205
  }
@@ -19,8 +19,7 @@
19
19
 
20
20
  const { Transform } = require('stream');
21
21
  const {
22
- extractObject,
23
- changeObjectNamingFromLowerCamelToSnake,
22
+ getFilterAndStringifyFn,
24
23
  getLogger,
25
24
  } = require('../../components/utils.js');
26
25
 
@@ -37,22 +36,6 @@ const START_TAG = '"results":';
37
36
  const FIELD_MASK_TAG = '"fieldMask"';
38
37
  const END_TAG = '"requestId"';
39
38
 
40
- /**
41
- * Generates a function that can convert a given JSON object to a JSON string
42
- * with only specified fields(fieldMask), in specified naming convention.
43
- * @param {string} fieldMask The 'fieldMask' string from response.
44
- * @param {boolean=} snakeCase Whether or not output JSON in snake naming.
45
- */
46
- function generateProcessFn(fieldMask, snakeCase = false) {
47
- const extractor = extractObject(fieldMask.split(','));
48
- return (originalObject) => {
49
- const extracted = extractor(originalObject);
50
- const generatedObject = snakeCase
51
- ? changeObjectNamingFromLowerCamelToSnake(extracted) : extracted;
52
- return JSON.stringify(generatedObject);
53
- };
54
- };
55
-
56
39
  /**
57
40
  * A stream.Transform that can extract properties and convert naming of the
58
41
  * reponse of Google/Search Ads report from REST interface.
@@ -85,7 +68,7 @@ class RestSearchStreamTransform extends Transform {
85
68
  .substring(maskIndex + FIELD_MASK_TAG.length, rawString.indexOf(END_TAG))
86
69
  .split('"')[1];
87
70
  this.logger.debug(`Got fieldMask: ${fieldMask}`);
88
- this.processFn = generateProcessFn(fieldMask, this.snakeCase);
71
+ this.processFn = getFilterAndStringifyFn(fieldMask, this.snakeCase);
89
72
  }
90
73
  const resultsWithTailing = rawString.substring(startIndex, maskIndex);
91
74
  const results = resultsWithTailing.substring(
@@ -0,0 +1,55 @@
1
+ // Copyright 2023 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 A base class for Google Api client library class.
17
+ */
18
+ const { google } = require('googleapis');
19
+ const { getLogger, getObjectByPath } = require('../../components/utils.js');
20
+ const { AuthRestfulApi } = require('./auth_restful_api.js');
21
+
22
+ /**
23
+ * A Google Api client library class.
24
+ */
25
+ class GoogleApiClient extends AuthRestfulApi {
26
+
27
+ /** @constructor */
28
+ constructor(env = process.env, options = {}) {
29
+ super(env, options);
30
+ this.logger = getLogger('API.default');
31
+ }
32
+
33
+ /**
34
+ * Returns the Api version of the Api in the current library.
35
+ * @return {string}
36
+ * @abstract
37
+ */
38
+ getVersion() { }
39
+
40
+ /**
41
+ * Returns the Api instance.
42
+ * @return {!Promise<object>} The Api instance.
43
+ */
44
+ async getApiClient() {
45
+ if (this.apiClient) return this.apiClient;
46
+ this.logger.info(`Initialized ${this.constructor.name} instance.`);
47
+ this.apiClient = google[this.googleApi]({
48
+ version: this.getVersion(),
49
+ auth: await this.getAuth(),
50
+ });
51
+ return this.apiClient;
52
+ }
53
+ }
54
+
55
+ module.exports = { GoogleApiClient };
@@ -19,9 +19,8 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const {google} = require('googleapis');
23
- const {request} = require('gaxios');
24
- const AuthClient = require('./auth_client.js');
22
+ const { request } = require('gaxios');
23
+ const { GoogleApiClient } = require('./base/google_api_client.js');
25
24
  const {
26
25
  getLogger,
27
26
  getFilterFunction,
@@ -94,7 +93,7 @@ const MAX_IDENTIFIERS_PER_USER = 5;
94
93
  * Google DfaReport API v3.0 stub.
95
94
  * see https://developers.google.com/doubleclick-advertisers/service_accounts
96
95
  */
97
- class DfaReporting {
96
+ class DfaReporting extends GoogleApiClient {
98
97
 
99
98
  /**
100
99
  * @constructor
@@ -102,34 +101,19 @@ class DfaReporting {
102
101
  * variables.
103
102
  */
104
103
  constructor(env = process.env) {
105
- this.authClient = new AuthClient(API_SCOPES, env);
104
+ super(env);
105
+ this.googleApi = 'dfareporting';
106
106
  this.logger = getLogger('API.CM');
107
107
  }
108
108
 
109
- /**
110
- * Prepares the Google DfaReport API instance.
111
- * @return {!google.dfareporting}
112
- * @private
113
- */
114
- async getApiClient_() {
115
- if (this.dfareporting) return this.dfareporting;
116
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
117
- this.dfareporting = google.dfareporting({
118
- version: API_VERSION,
119
- auth: await this.getAuth_(),
120
- });
121
- return this.dfareporting;
109
+ /** @override */
110
+ getScope() {
111
+ return API_SCOPES;
122
112
  }
123
113
 
124
- /**
125
- * Gets the auth object.
126
- * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
127
- */
128
- async getAuth_() {
129
- if (this.auth) return this.auth;
130
- await this.authClient.prepareCredentials();
131
- this.auth = this.authClient.getDefaultAuth();
132
- return this.auth;
114
+ /** @override */
115
+ getVersion() {
116
+ return API_VERSION;
133
117
  }
134
118
 
135
119
  /**
@@ -140,7 +124,7 @@ class DfaReporting {
140
124
  * @return {!Promise<string>}
141
125
  */
142
126
  async getProfileId(accountId) {
143
- const dfareporting = await this.getApiClient_();
127
+ const dfareporting = await this.getApiClient();
144
128
  const { data: { items } } = await dfareporting.userProfiles.list();
145
129
  const profiles = items.filter(
146
130
  (profile) => profile.accountId === accountId
@@ -237,7 +221,7 @@ class DfaReporting {
237
221
  numberOfLines: lines.length,
238
222
  };
239
223
  try {
240
- const dfareporting = await this.getApiClient_();
224
+ const dfareporting = await this.getApiClient();
241
225
  const response = await dfareporting.conversions[operation]({
242
226
  profileId: config.profileId,
243
227
  requestBody: requestBody,
@@ -304,7 +288,7 @@ class DfaReporting {
304
288
  * @return {!Promise<!Array<string>>}
305
289
  */
306
290
  async listUserProfiles() {
307
- const dfareporting = await this.getApiClient_();
291
+ const dfareporting = await this.getApiClient();
308
292
  const { data: { items } } = await dfareporting.userProfiles.list();
309
293
  return items.map(({profileId, userName, accountId, accountName}) => {
310
294
  return `Profile: ${profileId}[${userName}] `
@@ -349,7 +333,7 @@ class DfaReporting {
349
333
  */
350
334
  async runReport(config) {
351
335
  const profileId = await this.getProfileForOperation_(config);
352
- const dfareporting = await this.getApiClient_();
336
+ const dfareporting = await this.getApiClient();
353
337
  const { startDate, endDate } = config;
354
338
  if (startDate && endDate) {
355
339
  const { data: report } = await dfareporting.reports.get({
@@ -392,7 +376,7 @@ class DfaReporting {
392
376
  */
393
377
  async getReportFileUrl(config) {
394
378
  const profileId = await this.getProfileForOperation_(config);
395
- const dfareporting = await this.getApiClient_();
379
+ const dfareporting = await this.getApiClient();
396
380
  const response = await dfareporting.reports.files.get({
397
381
  profileId,
398
382
  reportId: config.reportId,
@@ -410,7 +394,7 @@ class DfaReporting {
410
394
  * @return {!Promise<stream>}
411
395
  */
412
396
  async getReportFileStream(url) {
413
- const auth = await this.getAuth_();
397
+ const auth = await this.getAuth();
414
398
  const headers = await auth.getRequestHeaders();
415
399
  const response = await request({
416
400
  method: 'GET',
@@ -19,14 +19,8 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const { google } = require('googleapis');
23
- const AuthClient = require('./auth_client.js');
24
- const {
25
- getLogger,
26
- getObjectByPath,
27
- SendSingleBatch,
28
- BatchResult,
29
- } = require('../components/utils.js');
22
+ const { GoogleApiClient } = require('./base/google_api_client.js');
23
+ const { getLogger } = require('../components/utils.js');
30
24
 
31
25
  const API_SCOPES = Object.freeze([
32
26
  'https://www.googleapis.com/auth/display-video',
@@ -40,7 +34,7 @@ const API_VERSION = 'v3';
40
34
  * Bid Manager API.
41
35
  * @see https://developers.google.com/bid-manager/reference/rest
42
36
  */
43
- class DisplayVideo {
37
+ class DisplayVideo extends GoogleApiClient {
44
38
 
45
39
  /**
46
40
  * @constructor
@@ -48,44 +42,19 @@ class DisplayVideo {
48
42
  * variables.
49
43
  */
50
44
  constructor(env = process.env) {
51
- this.authClient = new AuthClient(API_SCOPES, env);
45
+ super(env);
46
+ this.googleApi = 'displayvideo';
52
47
  this.logger = getLogger('API.DV3API');
53
48
  }
54
49
 
55
- /**
56
- * Prepares the Google DV3 instance.
57
- * @return {!google.displayvideo}
58
- * @private
59
- */
60
- async getApiClient_() {
61
- if (this.displayvideo) return this.displayvideo;
62
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
63
- this.displayvideo = google.displayvideo({
64
- version: API_VERSION,
65
- auth: await this.getAuth_(),
66
- });
67
- return this.displayvideo;
68
- }
69
-
70
- /**
71
- * Gets the auth object.
72
- * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
73
- */
74
- async getAuth_() {
75
- if (this.auth) return this.auth;
76
- await this.authClient.prepareCredentials();
77
- this.auth = this.authClient.getDefaultAuth();
78
- return this.auth;
50
+ /** @override */
51
+ getScope() {
52
+ return API_SCOPES;
79
53
  }
80
54
 
81
- /**
82
- * Gets the instance of function object based on Google API client library.
83
- * @param {string|undefined} path
84
- * @return {Object}
85
- */
86
- async getFunctionObject(path) {
87
- const instance = await this.getApiClient_();
88
- return getObjectByPath(instance, path);
55
+ /** @override */
56
+ getVersion() {
57
+ return API_VERSION;
89
58
  }
90
59
 
91
60
  }
@@ -19,8 +19,7 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const {google} = require('googleapis');
23
- const AuthClient = require('./auth_client.js');
22
+ const { GoogleApiClient } = require('./base/google_api_client.js');
24
23
  const { getLogger } = require('../components/utils.js');
25
24
 
26
25
  const API_SCOPES = Object.freeze([
@@ -28,25 +27,24 @@ const API_SCOPES = Object.freeze([
28
27
  ]);
29
28
  const API_VERSION = 'v2';
30
29
 
31
- /**
32
- * The returned information of get a query.
33
- * @typedef {{
34
- * running:boolean,
35
- * latestReportRunTimeMs:string,
36
- * googleCloudStoragePathForLatestReport:string,
37
- * }}
38
- */
39
- let QueryResource;
40
-
41
30
  /**
42
31
  * RequestBody controls the data range of reports.
43
32
  * see:
44
- * https://developers.google.com/bid-manager/v1.1/queries/runquery#request-body
33
+ * https://developers.google.com/bid-manager/reference/rest/v2/queries/run#RunQueryRequest
45
34
  * @typedef {{
46
- * dataRange: string,
47
- * reportDataStartTimeMs: long,
48
- * reportDataEndTimeMs: long,
49
- * timezoneCode: string
35
+ * dataRange: {
36
+ * range: Range,
37
+ * customStartDate: {
38
+ * year: integer,
39
+ * month: integer,
40
+ * day: integer,
41
+ * },
42
+ * customEndDate: {
43
+ * year: integer,
44
+ * month: integer,
45
+ * day: integer,
46
+ * },
47
+ * }
50
48
  * }}
51
49
  */
52
50
  let RequestBody;
@@ -56,31 +54,26 @@ let RequestBody;
56
54
  * Note: DV360 report API only support OAuth 2.0, see:
57
55
  * https://developers.google.com/bid-manager/how-tos/authorizing
58
56
  */
59
- class DoubleClickBidManager {
57
+ class DoubleClickBidManager extends GoogleApiClient {
60
58
  /**
61
59
  * @constructor
62
60
  * @param {!Object<string,string>=} env The environment object to hold env
63
61
  * variables.
64
62
  */
65
63
  constructor(env = process.env) {
66
- this.authClient = new AuthClient(API_SCOPES, env);
64
+ super(env);
65
+ this.googleApi = 'doubleclickbidmanager';
67
66
  this.logger = getLogger('API.DV3');
68
67
  }
69
68
 
70
- /**
71
- * Prepares the Google DBM instance.
72
- * @return {!google.doubleclickbidmanager}
73
- * @private
74
- */
75
- async getApiClient_() {
76
- if (this.doubleclickbidmanager) return this.doubleclickbidmanager;
77
- await this.authClient.prepareCredentials();
78
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
79
- this.doubleclickbidmanager = google.doubleclickbidmanager({
80
- version: API_VERSION,
81
- auth: this.authClient.getDefaultAuth(),
82
- });
83
- return this.doubleclickbidmanager;
69
+ /** @override */
70
+ getScope() {
71
+ return API_SCOPES;
72
+ }
73
+
74
+ /** @override */
75
+ getVersion() {
76
+ return API_VERSION;
84
77
  }
85
78
 
86
79
  /**
@@ -91,21 +84,21 @@ class DoubleClickBidManager {
91
84
  * @return {!Promise<number>} Report Id.
92
85
  */
93
86
  async runQuery(queryId, requestBody = undefined) {
94
- const doubleclickbidmanager = await this.getApiClient_();
87
+ const doubleclickbidmanager = await this.getApiClient();
95
88
  const response = await doubleclickbidmanager.queries.run(
96
89
  {queryId, requestBody});
97
90
  return response.data.key.reportId;
98
91
  }
99
92
 
100
93
  /**
101
- * Gets a query resource.
94
+ * Gets a query metadata.
102
95
  * See https://developers.google.com/bid-manager/reference/rest/v2/queries/get
103
96
  * @param {number} queryId Id of the query.
104
- * @return {!Promise<!QueryResource>} Query resource, see
105
- * https://developers.google.com/bid-manager/reference/rest/v2/queries#Query
97
+ * @return {!Promise<!QueryMetadata>} Query metadata, see
98
+ * https://developers.google.com/bid-manager/reference/rest/v2/queries#QueryMetadata
106
99
  */
107
100
  async getQuery(queryId) {
108
- const doubleclickbidmanager = await this.getApiClient_();
101
+ const doubleclickbidmanager = await this.getApiClient();
109
102
  const response = await doubleclickbidmanager.queries.get({ queryId });
110
103
  return response.data.metadata;
111
104
  }
@@ -117,14 +110,14 @@ class DoubleClickBidManager {
117
110
  * @return {!Promise<number>} Id of created query.
118
111
  */
119
112
  async createQuery(query) {
120
- const doubleclickbidmanager = await this.getApiClient_();
113
+ const doubleclickbidmanager = await this.getApiClient();
121
114
  const response = await doubleclickbidmanager.queries.create(
122
115
  {requestBody: query});
123
116
  return response.data.queryId;
124
117
  }
125
118
 
126
119
  async getQueryReport(queryId, reportId) {
127
- const doubleclickbidmanager = await this.getApiClient_();
120
+ const doubleclickbidmanager = await this.getApiClient();
128
121
  const response = await doubleclickbidmanager.queries.reports.get(
129
122
  { queryId, reportId });
130
123
  return response.data.metadata;
@@ -136,7 +129,7 @@ class DoubleClickBidManager {
136
129
  * @return {!Promise<boolean>} Whether the query was deleted.
137
130
  */
138
131
  async deleteQuery(queryId) {
139
- const doubleclickbidmanager = await this.getApiClient_();
132
+ const doubleclickbidmanager = await this.getApiClient();
140
133
  try {
141
134
  const { status } = await doubleclickbidmanager.queries.delete({ queryId });
142
135
  return status === 200;
@@ -148,7 +141,6 @@ class DoubleClickBidManager {
148
141
  }
149
142
 
150
143
  module.exports = {
151
- QueryResource,
152
144
  RequestBody,
153
145
  DoubleClickBidManager,
154
146
  };
@@ -19,9 +19,8 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const {google} = require('googleapis');
23
22
  const {request} = require('gaxios');
24
- const AuthClient = require('./auth_client.js');
23
+ const { GoogleApiClient } = require('./base/google_api_client.js');
25
24
  const {
26
25
  getLogger,
27
26
  SendSingleBatch,
@@ -118,7 +117,7 @@ let ReportRequest;
118
117
  * Quota limits, see:
119
118
  * https://support.google.com/adsihc/answer/6346075?hl=en
120
119
  */
121
- class DoubleClickSearch {
120
+ class DoubleClickSearch extends GoogleApiClient {
122
121
 
123
122
  /**
124
123
  * @constructor
@@ -126,34 +125,19 @@ class DoubleClickSearch {
126
125
  * variables.
127
126
  */
128
127
  constructor(env = process.env) {
129
- this.authClient = new AuthClient(API_SCOPES, env);
128
+ super(env);
129
+ this.googleApi = 'doubleclicksearch';
130
130
  this.logger = getLogger('API.DS');
131
131
  }
132
132
 
133
- /**
134
- * Prepares the Google SA360 instance.
135
- * @return {!google.doubleclicksearch}
136
- * @private
137
- */
138
- async getApiClient_() {
139
- if (this.doubleclicksearch) return this.doubleclicksearch;
140
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
141
- this.doubleclicksearch = google.doubleclicksearch({
142
- version: API_VERSION,
143
- auth: await this.getAuth_(),
144
- });
145
- return this.doubleclicksearch;
133
+ /** @override */
134
+ getScope() {
135
+ return API_SCOPES;
146
136
  }
147
137
 
148
- /**
149
- * Gets the auth object.
150
- * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
151
- */
152
- async getAuth_() {
153
- if (this.auth) return this.auth;
154
- await this.authClient.prepareCredentials();
155
- this.auth = this.authClient.getDefaultAuth();
156
- return this.auth;
138
+ /** @override */
139
+ getVersion() {
140
+ return API_VERSION;
157
141
  }
158
142
 
159
143
  /**
@@ -172,7 +156,7 @@ class DoubleClickSearch {
172
156
  });
173
157
  this.logger.debug('Sending out availabilities', availabilities);
174
158
  try {
175
- const doubleclicksearch = await this.getApiClient_();
159
+ const doubleclicksearch = await this.getApiClient();
176
160
  const response = await doubleclicksearch.conversion.updateAvailability(
177
161
  {requestBody: {availabilities}});
178
162
  this.logger.debug('Get response: ', response);
@@ -221,7 +205,7 @@ class DoubleClickSearch {
221
205
  numberOfLines: lines.length,
222
206
  };
223
207
  try {
224
- const doubleclicksearch = await this.getApiClient_();
208
+ const doubleclicksearch = await this.getApiClient();
225
209
  const response = await doubleclicksearch.conversion.insert(
226
210
  {requestBody: {conversion: conversions}}
227
211
  );
@@ -322,7 +306,7 @@ class DoubleClickSearch {
322
306
  * @return {!Promise<string>}
323
307
  */
324
308
  async requestReports(requestBody) {
325
- const doubleclicksearch = await this.getApiClient_();
309
+ const doubleclicksearch = await this.getApiClient();
326
310
  const { status, data } = await doubleclicksearch.reports.request({ requestBody });
327
311
  if (status >= 200 && status < 300) {
328
312
  return data.id;
@@ -341,7 +325,7 @@ class DoubleClickSearch {
341
325
  * }>>}
342
326
  */
343
327
  async getReportUrls(reportId) {
344
- const doubleclicksearch = await this.getApiClient_();
328
+ const doubleclicksearch = await this.getApiClient();
345
329
  const { status, data } = await doubleclicksearch.reports.get({ reportId });
346
330
  switch (status) {
347
331
  case 200:
@@ -367,11 +351,11 @@ class DoubleClickSearch {
367
351
  * @return {!Promise<string>}
368
352
  */
369
353
  async getReportFile(reportId, reportFragment) {
370
- const doubleclicksearch = await this.getApiClient_();
354
+ const doubleclicksearch = await this.getApiClient();
371
355
  const response = await doubleclicksearch.reports.getFile(
372
356
  {reportId, reportFragment});
373
357
  if (response.status === 200) {
374
- return Buffer.from(await response.data.arrayBuffer()).toString();
358
+ return response.data;
375
359
  }
376
360
  const errorMsg =
377
361
  `Error in get file from reports: ${reportFragment}@${reportId}`;
@@ -387,7 +371,7 @@ class DoubleClickSearch {
387
371
  * @return {!Promise<ReadableStream>}
388
372
  */
389
373
  async getReportFileStream(url) {
390
- const auth = await this.getAuth_();
374
+ const auth = await this.getAuth();
391
375
  const headers = await auth.getRequestHeaders();
392
376
  const response = await request({
393
377
  method: 'GET',
package/src/apis/index.js CHANGED
@@ -88,7 +88,6 @@ exports.searchads = require('./search_ads.js');
88
88
  /**
89
89
  * APIs integration class for DoubleClick BidManager (DV360).
90
90
  * @const {{
91
- * QueryResource:!QueryResource,
92
91
  * DoubleClickBidManager:!DoubleClickBidManager,
93
92
  * }}
94
93
  */
@@ -20,7 +20,7 @@
20
20
 
21
21
  const { request: gaxiosRequest } = require('gaxios');
22
22
  const { google } = require('googleapis');
23
- const AuthClient = require('./auth_client.js');
23
+ const { GoogleApiClient } = require('./base/google_api_client.js');
24
24
  const { getLogger } = require('../components/utils.js');
25
25
  const { getCleanCid, RestSearchStreamTransform }
26
26
  = require('./base/ads_api_common.js');
@@ -35,7 +35,7 @@ const API_VERSION = 'v0';
35
35
  * Search Ads 360 Reporting API stub.
36
36
  * See: https://developers.google.com/search-ads/reporting/api/reference/release-notes
37
37
  */
38
- class SearchAds {
38
+ class SearchAds extends GoogleApiClient {
39
39
 
40
40
  /**
41
41
  * @constructor
@@ -43,10 +43,21 @@ class SearchAds {
43
43
  * variables.
44
44
  */
45
45
  constructor(env = process.env) {
46
- this.authClient = new AuthClient(API_SCOPES, env);
46
+ super(env);
47
+ this.googleApi = 'searchads360';
47
48
  this.logger = getLogger('API.SA');
48
49
  }
49
50
 
51
+ /** @override */
52
+ getScope() {
53
+ return API_SCOPES;
54
+ }
55
+
56
+ /** @override */
57
+ getVersion() {
58
+ return API_VERSION;
59
+ }
60
+
50
61
  /**
51
62
  * Prepares the Search Ads 360 Reporting API instance.
52
63
  * OAuth 2.0 application credentials is required for calling this API.
@@ -59,11 +70,11 @@ class SearchAds {
59
70
  * @return {!google.searchads360}
60
71
  * @private
61
72
  */
62
- async getApiClient_(loginCustomerId) {
73
+ async getApiClient(loginCustomerId) {
63
74
  this.logger.debug(`Initialized SA reporting for ${loginCustomerId}`);
64
75
  const options = {
65
- version: API_VERSION,
66
- auth: await this.getAuth_(),
76
+ version: this.getVersion(),
77
+ auth: await this.getAuth(),
67
78
  };
68
79
  if (loginCustomerId) {
69
80
  options.headers = { 'login-customer-id': getCleanCid(loginCustomerId) };
@@ -71,17 +82,6 @@ class SearchAds {
71
82
  return google.searchads360(options);
72
83
  }
73
84
 
74
- /**
75
- * Gets the auth object.
76
- * @return {!Promise<{!OAuth2Client|!JWT|!Compute}>}
77
- */
78
- async getAuth_() {
79
- if (this.auth) return this.auth;
80
- await this.authClient.prepareCredentials();
81
- this.auth = this.authClient.getDefaultAuth();
82
- return this.auth;
83
- }
84
-
85
85
  /**
86
86
  * Gets a report synchronously from a given Customer account.
87
87
  * If there is a `nextPageToken` in the response, it means the report is not
@@ -96,7 +96,7 @@ class SearchAds {
96
96
  * @see https://developers.google.com/search-ads/reporting/api/reference/rpc/google.ads.searchads360.v0.services#searchsearchads360response
97
97
  */
98
98
  async getPaginatedReport(customerId, loginCustomerId, query, options = {}) {
99
- const searchads = await this.getApiClient_(loginCustomerId);
99
+ const searchads = await this.getApiClient(loginCustomerId);
100
100
  const requestBody = Object.assign({
101
101
  query,
102
102
  pageSize: 10000,
@@ -120,7 +120,7 @@ class SearchAds {
120
120
  * @see https://developers.google.com/search-ads/reporting/api/reference/rest/search
121
121
  */
122
122
  async restStreamReport(customerId, loginCustomerId, query) {
123
- const auth = await this.getAuth_();
123
+ const auth = await this.getAuth();
124
124
  const headers = Object.assign(
125
125
  await auth.getRequestHeaders(), {
126
126
  'login-customer-id': getCleanCid(loginCustomerId),
@@ -166,7 +166,7 @@ class SearchAds {
166
166
  * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/searchAds360Fields#SearchAds360Field
167
167
  */
168
168
  async getReportField(fieldName) {
169
- const searchads = await this.getApiClient_();
169
+ const searchads = await this.getApiClient();
170
170
  const resourceName = `searchAds360Fields/${fieldName}`;
171
171
  const response =
172
172
  await searchads.searchAds360Fields.get({ resourceName });
@@ -185,7 +185,7 @@ class SearchAds {
185
185
  */
186
186
  async searchReportField(adFields,
187
187
  metadata = ['name', 'data_type', 'is_repeated', 'type_url',]) {
188
- const searchads = await this.getApiClient_();
188
+ const searchads = await this.getApiClient();
189
189
  const selectClause = metadata.join(',');
190
190
  const fields = adFields.join('","');
191
191
  const query = `SELECT ${selectClause} WHERE name IN ("${fields}")`;
@@ -203,7 +203,7 @@ class SearchAds {
203
203
  * @see https://developers.google.com/search-ads/reporting/api/reference/rest/v0/customers.customColumns#CustomColumn
204
204
  */
205
205
  async listCustomColumns(customerId, loginCustomerId) {
206
- const searchads = await this.getApiClient_(loginCustomerId);
206
+ const searchads = await this.getApiClient(loginCustomerId);
207
207
  const response = await searchads.customers.customColumns.list({ customerId });
208
208
  return response.data.customColumns;
209
209
  }
@@ -219,7 +219,7 @@ class SearchAds {
219
219
  */
220
220
  async getCustomColumn(columnId, customerId, loginCustomerId) {
221
221
  const resourceName = `customers/${customerId}/customColumns/${columnId}`;
222
- const searchads = await this.getApiClient_(loginCustomerId);
222
+ const searchads = await this.getApiClient(loginCustomerId);
223
223
  const response = await searchads.customers.customColumns.get({ resourceName });
224
224
  return response.data;
225
225
  }
@@ -19,6 +19,7 @@
19
19
  'use strict';
20
20
 
21
21
  const {google} = require('googleapis');
22
+ const { GoogleApiClient } = require('./base/google_api_client.js');
22
23
  const {Params$Resource$Spreadsheets$Get} = google.sheets;
23
24
  const AuthClient = require('./auth_client.js');
24
25
  const {getLogger, BatchResult} = require('../components/utils.js');
@@ -63,7 +64,7 @@ let DimensionRange;
63
64
  /**
64
65
  * Google Spreadsheets API v4 stub.
65
66
  */
66
- class Spreadsheets {
67
+ class Spreadsheets extends GoogleApiClient {
67
68
  /**
68
69
  * Init Spreadsheets API client.
69
70
  * @param {string} spreadsheetId
@@ -71,29 +72,21 @@ class Spreadsheets {
71
72
  * variables.
72
73
  */
73
74
  constructor(spreadsheetId, env = process.env) {
75
+ super(env);
76
+ this.googleApi = 'sheets';
74
77
  /** @const {string} */
75
78
  this.spreadsheetId = spreadsheetId;
76
- this.authClient = new AuthClient(API_SCOPES, env);
77
- /**
78
- * Logger object from 'log4js' package where this type is not exported.
79
- */
80
79
  this.logger = getLogger('API.GS');
81
80
  }
82
81
 
83
- /**
84
- * Prepares the Google Sheets instance.
85
- * @return {!google.sheets}
86
- * @private
87
- */
88
- async getApiClient_() {
89
- if (this.sheets) return this.sheets;
90
- await this.authClient.prepareCredentials();
91
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
92
- this.sheets = google.sheets({
93
- version: API_VERSION,
94
- auth: this.authClient.getDefaultAuth(),
95
- });
96
- return this.sheets;
82
+ /** @override */
83
+ getScope() {
84
+ return API_SCOPES;
85
+ }
86
+
87
+ /** @override */
88
+ getVersion() {
89
+ return API_VERSION;
97
90
  }
98
91
 
99
92
  /**
@@ -108,7 +101,7 @@ class Spreadsheets {
108
101
  spreadsheetId: this.spreadsheetId,
109
102
  ranges: sheetName,
110
103
  };
111
- const sheets = await this.getApiClient_();
104
+ const sheets = await this.getApiClient();
112
105
  const response = await sheets.spreadsheets.get(request);
113
106
  const sheet = response.data.sheets[0];
114
107
  this.logger.debug(`Get sheet[${sheetName}]: `, sheet);
@@ -127,7 +120,7 @@ class Spreadsheets {
127
120
  range: sheetName,
128
121
  };
129
122
  try {
130
- const sheets = await this.getApiClient_();
123
+ const sheets = await this.getApiClient();
131
124
  const response = await sheets.spreadsheets.values.clear(request);
132
125
  const data = response.data;
133
126
  this.logger.debug(`Clear sheet[${sheetName}}]: `, data);
@@ -180,7 +173,7 @@ class Spreadsheets {
180
173
  ranges: sheetName,
181
174
  };
182
175
  try {
183
- const sheets = await this.getApiClient_();
176
+ const sheets = await this.getApiClient();
184
177
  const response = await sheets.spreadsheets.get(request);
185
178
  const sheet = response.data.sheets[0];
186
179
  const sheetId = sheet.properties.sheetId;
@@ -232,7 +225,7 @@ class Spreadsheets {
232
225
  numberOfLines: data.trim().split('\n').length,
233
226
  };
234
227
  try {
235
- const sheets = await this.getApiClient_();
228
+ const sheets = await this.getApiClient();
236
229
  const response = await sheets.spreadsheets.batchUpdate(request);
237
230
  const data = response.data;
238
231
  this.logger.debug(`Batch[${batchId}] uploaded: `, data);
@@ -19,6 +19,7 @@
19
19
  'use strict';
20
20
 
21
21
  const {google} = require('googleapis');
22
+ const { GoogleApiClient } = require('./base/google_api_client.js');
22
23
  const {
23
24
  Schema$Channel,
24
25
  Schema$Video,
@@ -26,8 +27,7 @@ const {
26
27
  Schema$Playlist,
27
28
  Schema$Search,
28
29
  } = google.youtube;
29
- const AuthClient = require('./auth_client.js');
30
- const {getLogger} = require('../components/utils.js');
30
+ const { getLogger } = require('../components/utils.js');
31
31
 
32
32
  const API_SCOPES = Object.freeze([
33
33
  'https://www.googleapis.com/auth/youtube.force-ssl'
@@ -159,34 +159,26 @@ let ListSearchConfig;
159
159
  * Search list type definition, see:
160
160
  * https://developers.google.com/youtube/v3/docs/search/list
161
161
  */
162
- class YouTube {
162
+ class YouTube extends GoogleApiClient {
163
163
  /**
164
164
  * @constructor
165
165
  * @param {!Object<string,string>=} env The environment object to hold env
166
166
  * variables.
167
167
  */
168
168
  constructor(env = process.env) {
169
- this.authClient = new AuthClient(API_SCOPES, env);
170
- /**
171
- * Logger object from 'log4js' package where this type is not exported.
172
- */
169
+ super(env);
170
+ this.googleApi = 'youtube';
173
171
  this.logger = getLogger('API.YT');
174
172
  }
175
173
 
176
- /**
177
- * Prepares the Google YouTube instance.
178
- * @return {!google.youtube}
179
- * @private
180
- */
181
- async getApiClient_() {
182
- if (this.youtube) return this.youtube;
183
- await this.authClient.prepareCredentials();
184
- this.logger.debug(`Initialized ${this.constructor.name} instance.`);
185
- this.youtube = google.youtube({
186
- version: API_VERSION,
187
- auth: this.authClient.getDefaultAuth(),
188
- });
189
- return this.youtube;
174
+ /** @override */
175
+ getScope() {
176
+ return API_SCOPES;
177
+ }
178
+
179
+ /** @override */
180
+ getVersion() {
181
+ return API_VERSION;
190
182
  }
191
183
 
192
184
  /**
@@ -200,7 +192,7 @@ class YouTube {
200
192
  const channelListRequest = Object.assign({}, config);
201
193
  channelListRequest.part = channelListRequest.part.join(',')
202
194
  try {
203
- const youtube = await this.getApiClient_();
195
+ const youtube = await this.getApiClient();
204
196
  const response = await youtube.channels.list(channelListRequest);
205
197
  this.logger.debug('Response: ', response);
206
198
  return response.data.items;
@@ -223,7 +215,7 @@ class YouTube {
223
215
  const videoListRequest = Object.assign({}, config);
224
216
  videoListRequest.part = videoListRequest.part.join(',')
225
217
  try {
226
- const youtube = await this.getApiClient_();
218
+ const youtube = await this.getApiClient();
227
219
  const response = await youtube.videos.list(videoListRequest);
228
220
  this.logger.debug('Response: ', response);
229
221
  return response.data.items;
@@ -247,7 +239,7 @@ class YouTube {
247
239
  const commentThreadsRequest = Object.assign({}, config);
248
240
  commentThreadsRequest.part = commentThreadsRequest.part.join(',')
249
241
  try {
250
- const youtube = await this.getApiClient_();
242
+ const youtube = await this.getApiClient();
251
243
  const response = await youtube.commentThreads.list(commentThreadsRequest);
252
244
  this.logger.debug('Response: ', response.data);
253
245
  return response.data.items;
@@ -282,7 +274,7 @@ class YouTube {
282
274
  }
283
275
 
284
276
  try {
285
- const youtube = await this.getApiClient_();
277
+ const youtube = await this.getApiClient();
286
278
  const response = await youtube.playlists.list(playlistsRequest);
287
279
  this.logger.debug('Response: ', response.data);
288
280
  if (response.data.nextPageToken) {
@@ -324,7 +316,7 @@ class YouTube {
324
316
  }
325
317
 
326
318
  try {
327
- const youtube = await this.getApiClient_();
319
+ const youtube = await this.getApiClient();
328
320
  const response = await youtube.search.list(searchRequest);
329
321
  this.logger.debug('Response: ', response.data);
330
322
  if (response.data.nextPageToken) {
@@ -75,7 +75,7 @@ class DatastoreModeAccess {
75
75
  */
76
76
  getKey(id) {
77
77
  const keyPath = [this.kind];
78
- if (id) keyPath.push(isNaN(id) ? id : parseInt(id));
78
+ if (id) keyPath.push(isNaN(id) ? id : this.datastore.int(id));
79
79
  return this.datastore.key({
80
80
  namespace: this.namespace,
81
81
  path: keyPath,
@@ -116,10 +116,11 @@ class DatastoreModeAccess {
116
116
  const key = this.getKey(id);
117
117
  const apiResponse =
118
118
  await this.datastore.save({ key, data, excludeLargeProperties: true });
119
- // Default key in Datastore is a number in response like following.
119
+ // Default key in Datastore is an int as string in response. It could be
120
+ // larger than JavaScript max safe integer, so keep it as string here.
120
121
  // With a given id, the key in response is null.
121
122
  const updatedId = id !== undefined ? id
122
- : +apiResponse[0].mutationResults[0].key.path[0].id;
123
+ : apiResponse[0].mutationResults[0].key.path[0].id;
123
124
  this.logger.debug(`Result of saving ${updatedId}@${this.kind}: `,
124
125
  JSON.stringify(apiResponse));
125
126
  // Datastore has a delay to write entity. This method only returns id
@@ -487,7 +487,7 @@ const extractObject = (paths) => {
487
487
  return (sourceObject) => {
488
488
  const output = {};
489
489
  paths.forEach((path) => {
490
- const [value, owner, property] = path.split('.')
490
+ const [value, owner, property] = path.trim().split('.')
491
491
  .reduce(transcribe, [sourceObject, output, undefined]);
492
492
  if (typeof value !== 'undefined') {
493
493
  owner[property] = value;
@@ -509,7 +509,7 @@ const getObjectByPath = (obj, paths) => {
509
509
  paths.split('.').filter((key) => !!key).forEach((key) => {
510
510
  instance = instance[key];
511
511
  if (!instance) {
512
- console.error('Fail to get function containter', paths);
512
+ console.error('Fail to get element from path:', paths);
513
513
  return instance;
514
514
  }
515
515
  });
@@ -591,7 +591,24 @@ const changeObjectNamingFromLowerCamelToSnake = (obj) => {
591
591
  } else {
592
592
  return obj;
593
593
  }
594
- }
594
+ };
595
+
596
+ /**
597
+ * Generates a function that can convert a given JSON object to a JSON string
598
+ * with only specified fields(fieldMask), in specified naming convention.
599
+ * @param {string} fieldMask The 'fieldMask' string from response.
600
+ * @param {boolean=} snakeCase Whether or not output JSON in snake naming.
601
+ */
602
+ const getFilterAndStringifyFn = (fieldMask, snakeCase = false) => {
603
+ const extractor = extractObject(
604
+ Array.isArray(fieldMask) ? fieldMask : fieldMask.split(','));
605
+ return (originalObject) => {
606
+ const extracted = extractor(originalObject);
607
+ const generatedObject = snakeCase
608
+ ? changeObjectNamingFromLowerCamelToSnake(extracted) : extracted;
609
+ return JSON.stringify(generatedObject);
610
+ };
611
+ };
595
612
 
596
613
  /**
597
614
  * Returns the response data for a HTTP request. It will retry the specific
@@ -619,7 +636,7 @@ const requestWithRetry = async (options, logger = console, retryTimes = 3) => {
619
636
  logger.error(`Request ${JSON.stringify(options)}`, error);
620
637
  }
621
638
  } while (processedTimes <= retryTimes)
622
- }
639
+ };
623
640
 
624
641
  // noinspection JSUnusedAssignment
625
642
  module.exports = {
@@ -641,5 +658,6 @@ module.exports = {
641
658
  changeNamingFromLowerCamelToSnake,
642
659
  changeObjectNamingFromSnakeToLowerCamel,
643
660
  changeObjectNamingFromLowerCamelToSnake,
661
+ getFilterAndStringifyFn,
644
662
  requestWithRetry,
645
663
  };