@google-cloud/nodejs-common 1.9.1 → 2.0.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.
@@ -1559,6 +1559,7 @@ set_cloud_functions_default_settings() {
1559
1559
  default_cf_flag+=(--set-env-vars=PROJECT_NAMESPACE="${PROJECT_NAMESPACE}")
1560
1560
  default_cf_flag+=(--set-env-vars=DEBUG="${DEBUG}")
1561
1561
  default_cf_flag+=(--set-env-vars=IN_GCP="${IN_GCP}")
1562
+ default_cf_flag+=(--set-env-vars=DATABASE_ID="${DATABASE_ID}")
1562
1563
  }
1563
1564
 
1564
1565
  #######################################
@@ -1571,6 +1572,9 @@ set_cloud_functions_default_settings() {
1571
1572
  #######################################
1572
1573
  set_authentication_env_for_cloud_functions() {
1573
1574
  local -n default_cf_flag=$1
1575
+ if [[ ! -z "${SECRET_NAME}" ]]; then
1576
+ cf_flag+=(--set-env-vars=SECRET_NAME="${SECRET_NAME}")
1577
+ fi
1574
1578
  if [[ -f "${SA_KEY_FILE}" ]]; then
1575
1579
  cf_flag+=(--set-env-vars=API_SERVICE_ACCOUNT="${SA_KEY_FILE}")
1576
1580
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@google-cloud/nodejs-common",
3
- "version": "1.9.1",
3
+ "version": "2.0.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",
@@ -19,7 +19,7 @@
19
19
  "@google-cloud/aiplatform": "^3.0.0",
20
20
  "@google-cloud/automl": "^4.0.0",
21
21
  "@google-cloud/bigquery": "^7.2.0",
22
- "@google-cloud/datastore": "^8.0.0",
22
+ "@google-cloud/datastore": "^8.2.2",
23
23
  "@google-cloud/firestore": "^6.7.0",
24
24
  "@google-cloud/logging-winston": "^6.0.0",
25
25
  "@google-cloud/pubsub": "^4.0.2",
@@ -108,7 +108,9 @@ class AdsDataHub extends AuthRestfulApi {
108
108
  * to the specified BigQuery destination table. The returned operation name
109
109
  * can be used to poll for query completion status.
110
110
  * @see https://developers.google.com/ads-data-hub/reference/rest/v1/customers.analysisQueries/startTransient
111
- * @param {string} queryText The content of the query.
111
+ * @param {!AnalysisQuery} query An analysis query that can be executed within
112
+ * Ads Data Hub. Its properties include 'queryText', 'parameterTypes', etc.
113
+ * @see https://developers.google.com/ads-data-hub/reference/rest/v1/customers.analysisQueries#AnalysisQuery
112
114
  * @param {Object} spec Defines the query execution parameters.
113
115
  * @see https://developers.google.com/ads-data-hub/reference/rest/v1/QueryExecutionSpec
114
116
  * @param {string} destTable Destination BigQuery table for query results with
@@ -121,9 +123,9 @@ class AdsDataHub extends AuthRestfulApi {
121
123
  * @return {!Promise<Object>} Promised operation object.
122
124
  * @see https://developers.google.com/ads-data-hub/reference/rest/v1/operations#Operation
123
125
  */
124
- async startTransientQuery(queryText, spec, destTable, customerId = this.customerId) {
126
+ async startTransientQuery(query, spec, destTable, customerId = this.customerId) {
125
127
  const path = `customers/${customerId}/analysisQueries:startTransient`;
126
- const data = { query: { queryText }, spec, destTable };
128
+ const data = { query, spec, destTable };
127
129
  const response = await this.request(path, 'POST', data);
128
130
  return response.data;
129
131
  }
@@ -16,13 +16,25 @@
16
16
  * @fileoverview A base class for RESTful API with authorization client.
17
17
  */
18
18
 
19
- const { RestfuleApiBase } = require('./restful_api_base');
19
+ const { RestfulApiBase } = require('./restful_api_base');
20
20
  const AuthClient = require('../auth_client.js');
21
21
 
22
22
  /**
23
23
  * A RESTful API class with authorization client.
24
24
  */
25
- class AuthRestfulApi extends RestfuleApiBase {
25
+ class AuthRestfulApi extends RestfulApiBase {
26
+
27
+ constructor(env = process.env, options = {}) {
28
+ super(env);
29
+ /**
30
+ * `authClient` can be consumed by cloud client library as the auth
31
+ * client. By passing this in, we can offer more flexible auth clients in
32
+ * test cases for API client library and cloud client library in future.
33
+ */
34
+ if (options.authClient) {
35
+ this.auth = options.authClient;
36
+ }
37
+ }
26
38
 
27
39
  /**
28
40
  * Returns the Api scope for authorization.
@@ -22,6 +22,8 @@ const {
22
22
  GaxiosOptions,
23
23
  } = require('gaxios');
24
24
 
25
+ const { getLogger } = require('../../components/utils.js');
26
+
25
27
  /**
26
28
  * A type for an Api response which is independent to the 'request' library.
27
29
  * @typedef {{
@@ -35,10 +37,11 @@ let Response;
35
37
  * Base class for RESTful API based on gaxios.
36
38
  * @see https://github.com/googleapis/gaxios
37
39
  */
38
- class RestfuleApiBase {
40
+ class RestfulApiBase {
39
41
 
40
42
  constructor(env = process.env) {
41
43
  this.env = env;
44
+ this.logger = getLogger(this.constructor.name);
42
45
  }
43
46
 
44
47
  /**
@@ -46,7 +49,7 @@ class RestfuleApiBase {
46
49
  * @return {string}
47
50
  * @abstract
48
51
  */
49
- getBaseUrl() { }
52
+ async getBaseUrl() { }
50
53
 
51
54
  /**
52
55
  * Returns default HTTP headers.
@@ -93,7 +96,7 @@ class RestfuleApiBase {
93
96
  const options = Object.assign(
94
97
  this.getRequesterOptions(),
95
98
  {
96
- url: this.getRequestUrl(uri),
99
+ url: await this.getRequestUrl(uri),
97
100
  method,
98
101
  headers: headers || await this.getDefaultHeaders(),
99
102
  data,
@@ -111,8 +114,8 @@ class RestfuleApiBase {
111
114
  * @param {string} requestUri The URI of the specific resource to request.
112
115
  * @return {string} representing the fully-qualified API URL.
113
116
  */
114
- getRequestUrl(requestUri) {
115
- const baseUrl = this.getBaseUrl();
117
+ async getRequestUrl(requestUri) {
118
+ const baseUrl = await this.getBaseUrl();
116
119
  if (requestUri) {
117
120
  if (requestUri.toLocaleLowerCase().startsWith('http')) return requestUri;
118
121
  return `${baseUrl}/${requestUri}`;
@@ -139,5 +142,5 @@ class RestfuleApiBase {
139
142
 
140
143
  module.exports = {
141
144
  Response,
142
- RestfuleApiBase,
145
+ RestfulApiBase,
143
146
  };
@@ -0,0 +1,79 @@
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 The base class of Google Cloud Apis.
17
+ */
18
+ const AuthClient = require('../auth_client.js');
19
+ const { AuthRestfulApi } = require('../base/auth_restful_api.js');
20
+
21
+ /**
22
+ * A RESTful API class for Google Cloud Platform API with ADC as the
23
+ * authorization method.
24
+ */
25
+ class GoogleCloudApi extends AuthRestfulApi {
26
+
27
+ constructor(env = process.env, options = {}) {
28
+ super(env, options);
29
+ this.projectId = options.projectId || env['GCP_PROJECT'];
30
+ }
31
+
32
+ /** @override */
33
+ getScope() {
34
+ return ['https://www.googleapis.com/auth/cloud-platform'];
35
+ }
36
+
37
+ /**
38
+ * @override
39
+ * Returns the `GoogleAuth` object for a Google Cloud component object.
40
+ * If the component is based on Google Cloud Client Library, it will use the
41
+ * `ADC` auth object by itself. However, some components are based on Google
42
+ * API Client Library, which is based on API Scope and an explicit auth
43
+ * object. In this situation, this function can help to generate such an auth
44
+ * object.
45
+ * By default, `AuthClient` (getDefaultAuth()) will return an auth client
46
+ * based on the settings in ENV while the OAuth is the most preferred.
47
+ * This works for most of the external API clients (in the '../apis'
48
+ * folder), however this won't work in the Cloud Functions, as those OAuth
49
+ * token usually won't have enough permission to invoke Google Cloud API.
50
+ * Using the method `getApplicationDefaultCredentials` to force
51
+ * `AuthClient` return an ADC auth client, which will work in the Cloud.
52
+ */
53
+ async getAuth() {
54
+ if (this.auth) return this.auth;
55
+ this.authClient = new AuthClient(this.getScope(), this.env);
56
+ // Not required for ADC: await this.authClient.prepareCredentials();
57
+ this.auth = this.authClient.getApplicationDefaultCredentials();
58
+ return this.auth;
59
+ }
60
+
61
+ /**
62
+ * Gets the GCP project Id. In Cloud Functions, it *should* be passed in
63
+ * through environment variable during the deployment. But if it doesn't exist
64
+ * (for example, in local unit tests), this function will fallback to ADC
65
+ * (Application Default Credential) auth's asynchronous function to get the
66
+ * project Id.
67
+ * @return {string}
68
+ */
69
+ async getProjectId() {
70
+ if (!this.projectId) {
71
+ const auth = await this.getAuth();
72
+ this.projectId = await auth.getProjectId();
73
+ }
74
+ return this.projectId;
75
+ }
76
+
77
+ }
78
+
79
+ module.exports = { GoogleCloudApi };
@@ -391,18 +391,18 @@ class DfaReporting {
391
391
  throw new Error(`Unsupported report status: ${data.status}`);
392
392
  }
393
393
 
394
- //TODO(lushu) check the response for very big file.
395
394
  /**
396
- * Downloads the report file.
395
+ * Gets the stream of the report file.
397
396
  * @param {string} url
398
- * @return {!Promise<string>}
397
+ * @return {!Promise<stream>}
399
398
  */
400
- async downloadReportFile(url) {
399
+ async getReportFileStream(url) {
401
400
  const auth = await this.getAuth_();
402
401
  const headers = await auth.getRequestHeaders();
403
402
  const response = await request({
404
403
  method: 'GET',
405
404
  headers,
405
+ responseType: 'stream',
406
406
  url,
407
407
  });
408
408
  return response.data;
@@ -18,7 +18,7 @@
18
18
 
19
19
  'use strict';
20
20
 
21
- const { RestfuleApiBase } = require('./base/restful_api_base.js');
21
+ const { RestfulApiBase } = require('./base/restful_api_base.js');
22
22
  const { getLogger } = require('../components/utils.js');
23
23
 
24
24
  const API_VERSION = 'v3';
@@ -28,7 +28,7 @@ const API_ENDPOINT = 'https://api.sendgrid.com';
28
28
  * SendGrid API access class.
29
29
  * @see https://docs.sendgrid.com/api-reference/how-to-use-the-sendgrid-v3-api/authentication
30
30
  */
31
- class SendGrid extends RestfuleApiBase {
31
+ class SendGrid extends RestfulApiBase {
32
32
 
33
33
  constructor(apiKey, env = process.env) {
34
34
  super(env);
@@ -27,6 +27,10 @@ const {
27
27
  DocumentData,
28
28
  Transaction: NativeModeTransaction,
29
29
  } = require('@google-cloud/firestore');
30
+ const {
31
+ FirestoreApi,
32
+ DEFAULT_DATABASE,
33
+ } = require('./firestore_api.js');
30
34
 
31
35
  /**
32
36
  * Document in Native mode or Entity in Datastore mode.
@@ -122,6 +126,17 @@ const DataSource = Object.freeze({
122
126
  DATASTORE: 'datastore',
123
127
  });
124
128
 
129
+ /**
130
+ * Definition of a database. Currently, it supports:
131
+ * 1. Firestore Native mode
132
+ * 2. Firestore Datastore mode
133
+ * @typedef {(
134
+ * source: !DataSource,
135
+ * id: string,
136
+ * )}
137
+ */
138
+ let Database;
139
+
125
140
  /**
126
141
  * Firestore has two modes: 'Native' and 'Datastore'. Only one of them can be
127
142
  * set in a Cloud Project and it can not be changed in the Cloud Project once
@@ -216,10 +231,33 @@ class FirestoreAccessBase {
216
231
 
217
232
  }
218
233
 
234
+ /**
235
+ * Returns whether the mode of Firestore is 'Native'.
236
+ * @param {string} projectId GCP project Id.
237
+ * @param {string=} databaseId GCP project Id.
238
+ * @return {!Promise<!Database>}
239
+ */
240
+ async function getFirestoreDatabase(projectId, databaseId = DEFAULT_DATABASE) {
241
+ try {
242
+ const firestore = new FirestoreApi(process.env, { projectId, databaseId });
243
+ const type = await firestore.getFirestoreMode();
244
+ console.log(`Get Firestore ${databaseId}@${projectId}, mode: ${type}`);
245
+ return {
246
+ source: DataSource[type],
247
+ id: firestore.databaseId,
248
+ };
249
+ } catch (error) {
250
+ console.error(`Failed to get Firestore ${databaseId}@${projectId}: `,
251
+ error.message);
252
+ throw error;
253
+ }
254
+ };
255
+
219
256
  /**
220
257
  * Returns whether the mode of Firestore is 'Native'.
221
258
  * @param {string=} projectId GCP project Id.
222
259
  * @return {!Promise<boolean>}
260
+ * @deprecated
223
261
  */
224
262
  async function isNativeMode(projectId = process.env['GCP_PROJECT']) {
225
263
  try {
@@ -237,8 +275,12 @@ module.exports = {
237
275
  Transaction,
238
276
  Filter,
239
277
  TransactionOperation,
278
+ Database,
240
279
  DatastoreDocumentFacade,
241
280
  DatastoreTransactionFacade,
242
281
  FirestoreAccessBase,
282
+ getFirestoreDatabase,
283
+ DEFAULT_DATABASE,
284
+ FirestoreApi,
243
285
  isNativeMode,
244
286
  };
@@ -25,6 +25,8 @@ const {
25
25
  Filter,
26
26
  TemplatedTxFunction,
27
27
  FirestoreAccessBase,
28
+ Database,
29
+ DEFAULT_DATABASE,
28
30
  } = require('./access_base.js');
29
31
  const NativeModeAccess = require('./native_mode_access.js');
30
32
  const DatastoreModeAccess = require('./datastore_mode_access.js');
@@ -33,8 +35,7 @@ const DatastoreModeAccess = require('./datastore_mode_access.js');
33
35
  * This is data access object base class on Firestore. It seals the details of
34
36
  * different underlying databases, Firestore and Datastore.
35
37
  * This class relies on an initial parameter in construtor to indicate the
36
- * Firestore type. If omitted, this parameter will take the value of the
37
- * environment variable named 'FIRESTORE_TYPE'.
38
+ * Firestore type.
38
39
  *
39
40
  * Firestore and Datastore have different transaction APIs. In that case,
40
41
  * separate DAOs are required to offer different functions in each mode.
@@ -45,22 +46,24 @@ class DataAccessObject {
45
46
  * Initializes the instance based on given data source.
46
47
  * @param {string} kind The data model name.
47
48
  * @param {string} namespace The namespace of the data.
48
- * @param {!DataSource} dataSource The data source type.
49
+ * @param {!Database|!DataSource} database The database or data base type.
49
50
  * @param {string} projectId The Id of Cloud project.
50
51
  */
51
- constructor(kind, namespace, dataSource = process.env['FIRESTORE_TYPE'],
52
- projectId = process.env['GCP_PROJECT']) {
52
+ constructor(kind, namespace, database,
53
+ projectId = process.env['GCP_PROJECT']) {
53
54
  /** @const {string} */ this.namespace = namespace;
54
- /** @const {!DataSource} */ this.dataSource = dataSource;
55
+ /** @const {!DataSource} */ this.dataSource =
56
+ typeof database === 'string' ? database : database.source;
57
+ const databaseId = database.id || DEFAULT_DATABASE;
55
58
  /** @type {!FirestoreAccessBase} */ this.accessObject = undefined;
56
59
  switch (this.dataSource) {
57
60
  case DataSource.FIRESTORE:
58
61
  this.accessObject = new NativeModeAccess(
59
- `${this.namespace}/database/${kind}`, projectId);
62
+ `${this.namespace}/database/${kind}`, projectId, databaseId);
60
63
  break;
61
64
  case DataSource.DATASTORE:
62
65
  this.accessObject = new DatastoreModeAccess(this.namespace, kind,
63
- projectId);
66
+ projectId, databaseId);
64
67
  break;
65
68
  default:
66
69
  throw new Error(`Unknown DataSource item: ${this.dataSource}.`);
@@ -29,6 +29,7 @@ const {
29
29
  DatastoreDocumentFacade,
30
30
  DatastoreTransactionFacade,
31
31
  FirestoreAccessBase,
32
+ DEFAULT_DATABASE,
32
33
  } = require('./access_base.js');
33
34
  const {getLogger, wait} = require('../utils.js');
34
35
 
@@ -46,11 +47,15 @@ class DatastoreModeAccess {
46
47
  * Initializes DatastoreModeAccess instance.
47
48
  * @param {string} namespace The namespace for data.
48
49
  * @param {string} kind The kind of this entity.
49
- * @param {string} projectId The Id of Cloud project.
50
+ * @param {string=} projectId The Id of Cloud project.
51
+ * @param {string=} databaseId The Id of Firestore database.
50
52
  */
51
- constructor(namespace, kind, projectId = process.env['GCP_PROJECT']) {
53
+ constructor(namespace, kind, projectId = process.env['GCP_PROJECT'],
54
+ databaseId = process.env['DATABASE_ID'] || DEFAULT_DATABASE) {
55
+ // '(default)' is not allowed, use empty string '' to refer the default database.
56
+ const idForDatastore = databaseId === DEFAULT_DATABASE ? '' : databaseId;
52
57
  /** @type{Datastore} */
53
- this.datastore = new Datastore({projectId});
58
+ this.datastore = new Datastore({ projectId, databaseId: idForDatastore });
54
59
  this.kind = kind;
55
60
  this.namespace = namespace;
56
61
  this.logger = getLogger('DS.ACC');
@@ -0,0 +1,89 @@
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
+ const { GoogleCloudApi } = require('../../apis/cloud/google_cloud_api');
16
+
17
+ /**
18
+ * The name of default database.
19
+ * @see https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases#resource:-database
20
+ * @const {string}
21
+ */
22
+ const DEFAULT_DATABASE = '(default)';
23
+
24
+ /**
25
+ * The modes of Firestore. Mode changes are only allowed if the database is empty.
26
+ * @see https://cloud.google.com/datastore/docs/firestore-or-datastore
27
+ * @see https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases#DatabaseType
28
+ * @enum {string}
29
+ */
30
+ const FIRESTORE_MODE = Object.freeze({
31
+ FIRESTORE_NATIVE: 'FIRESTORE',
32
+ DATASTORE_MODE: 'DATASTORE',
33
+ });
34
+
35
+ class FirestoreApi extends GoogleCloudApi {
36
+
37
+ constructor(env = process.env, options = {}) {
38
+ super(env, options);
39
+ this.apiUrl = 'https://firestore.googleapis.com';
40
+ this.version = 'v1';
41
+ this.databaseId = options.databaseId || env.DATABASE_ID || DEFAULT_DATABASE;
42
+ }
43
+
44
+ /** @override */
45
+ async getBaseUrl() {
46
+ const projectId = await this.getProjectId();
47
+ return `${this.apiUrl}/${this.version}/projects/${projectId}`;
48
+ }
49
+
50
+ /**
51
+ * Gets the information of the default database.
52
+ * If the database is ready, will return:
53
+ * {
54
+ * data: {
55
+ * name:...
56
+ * type:...
57
+ * },
58
+ * code: 200
59
+ * }
60
+ * If the database is not created, will throw an error: "Request failed with
61
+ * status code 404"
62
+ * @see https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases/get
63
+ * @return {Database}
64
+ * @see https://cloud.google.com/firestore/docs/reference/rest/v1/projects.databases#Database
65
+ */
66
+ async getDatabase(databaseId = this.databaseId) {
67
+ return super.request(`databases/${databaseId}`);
68
+ }
69
+
70
+ /**
71
+ * Gets the mode of the Firestore database.
72
+ * @param {string=} databaseId
73
+ * @return {'FIRESTORE'|'DATASTORE'} Mode of the Firestore.
74
+ */
75
+ async getFirestoreMode(databaseId = this.databaseId) {
76
+ const response = await this.getDatabase(databaseId);
77
+ if (!response.data) {
78
+ this.logger.error(`Fail to find database ${databaseId}`, response);
79
+ throw new Error(`Fail to find database ${databaseId}`);
80
+ }
81
+ this.logger.debug(`Get ${databaseId} in mode ${response.data.type}.`);
82
+ return FIRESTORE_MODE[response.data.type];
83
+ }
84
+ }
85
+
86
+ module.exports = {
87
+ DEFAULT_DATABASE,
88
+ FirestoreApi,
89
+ };
@@ -24,7 +24,7 @@ const {
24
24
  CollectionReference,
25
25
  OrderByDirection,
26
26
  } = require('@google-cloud/firestore');
27
- const {FirestoreAccessBase} = require('./access_base.js');
27
+ const { FirestoreAccessBase, DEFAULT_DATABASE } = require('./access_base.js');
28
28
  const {getLogger} = require('../utils.js');
29
29
 
30
30
  /**
@@ -41,11 +41,13 @@ class NativeModeAccess {
41
41
  * way. This constructor will check the path to make sure it presents a
42
42
  * 'collection', otherwise an Error will be thrown.
43
43
  * @param {string} path Path for the 'collection'.
44
- * @param {string} projectId The Id of Cloud project.
44
+ * @param {string=} projectId The Id of Cloud project.
45
+ * @param {string=} databaseId The Id of Firestore database.
45
46
  */
46
- constructor(path, projectId = process.env['GCP_PROJECT']) {
47
+ constructor(path, projectId = process.env['GCP_PROJECT'],
48
+ databaseId = process.env['DATABASE_ID'] || DEFAULT_DATABASE) {
47
49
  /** @type {!Firestore} */
48
- this.firestore = new Firestore({projectId});
50
+ this.firestore = new Firestore({ projectId, databaseId });
49
51
  if (path.split('/').length % 2 === 0) {
50
52
  throw new Error(`Invalid path for Collection: ${path}`);
51
53
  }