@google-cloud/nodejs-common 1.7.9-alpha → 1.8.1-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.
@@ -66,75 +66,14 @@ let MainFunctionOfStorage;
66
66
  let EventContext;
67
67
 
68
68
  /**
69
- * The first parameter passed to Cloud Functions as the 'Event' parameter in
70
- * Node6 environment.
71
- * @typedef {{
72
- * data:(!StorageEventData|!PubsubMessage),
73
- * context:!EventContext,
74
- * }}
75
- */
76
- let CloudFunctionEvent;
77
-
78
- /**
79
- * Storage event driven Cloud Functions in Node6 environment. It takes one
80
- * parameters and returns a Promise, see:
81
- * https://cloud.google.com/functions/docs/writing/background#functions_background_parameters-node6
82
- * @typedef {function(!CloudFunctionEvent):!Promise<*>}
83
- * @deprecated due to node6 env has benn sun set.
84
- */
85
- let CloudFunctionNode6;
86
-
87
- /**
88
- * Storage event driven Cloud Functions in Node8/10 environment. It takes two
69
+ * Storage event driven Cloud Functions in Node.js environment. It takes two
89
70
  * parameters, see:
90
71
  * https://cloud.google.com/functions/docs/writing/background#functions_background_parameters-node8-10
91
72
  * @typedef{function((!StorageEventData|!PubsubMessage),!EventContext):
92
73
  * !Promise<*>}
93
74
  */
94
- let CloudFunctionNode8;
95
-
96
- /**
97
- * Cloud Functions triggered by Cloud Storage events.
98
- *
99
- * @typedef {(!CloudFunctionNode6|!CloudFunctionNode8)}
100
- */
101
75
  let CloudFunction;
102
76
 
103
- /**
104
- * Gets a Node6 compatible Cloud Functions based on a Node8/10 Cloud Functions.
105
- * Cloud Functions have different parameter types and numbers in Node6 and
106
- * Node8/10:
107
- * 1. Node6 (deprecated) has (event, callback);
108
- * 2. Node8/10 has (data, context, callback).
109
- * see
110
- * https://cloud.google.com/functions/docs/writing/background#functions-writing-background-hello-pubsub-node8-10
111
- *
112
- * @param {!CloudFunctionNode8} fn Cloud Functions in Node8 environment.
113
- * @return {!CloudFunction} The Cloud Functions can return any type.
114
- */
115
- const adaptNode6 = (fn) => {
116
- /**
117
- * Returns the Cloud Function for both Node6 and Node8/10:
118
- * @param {!CloudFunctionEvent|!PubsubMessage|!StorageEventData} eventOrData
119
- * The event payload for nodejs8/10 or The Cloud Functions event for
120
- * nodejs6.
121
- * @param {?EventContext} possibleContext The event metadata for nodejs8/10
122
- * or undefined for nodejs6.
123
- * @return {!Promise<*>}
124
- */
125
- return (eventOrData, possibleContext) => {
126
- const /** @type {!StorageEventData|!PubsubMessage} */ data =
127
- (!possibleContext)
128
- ? (/** @type {CloudFunctionEvent} */ eventOrData).data
129
- : eventOrData;
130
- const /** @type {!EventContext} */ context =
131
- (!possibleContext)
132
- ? (/** @type {CloudFunctionEvent} */ eventOrData).context
133
- : possibleContext;
134
- return fn(data, context);
135
- };
136
- };
137
-
138
77
  /**
139
78
  * Triggers the main function with the correct new coming file for once.
140
79
  * Detailed steps:
@@ -156,52 +95,45 @@ const adaptNode6 = (fn) => {
156
95
  const validatedStorageTrigger = (fn, folder, processed = 'processed/') => {
157
96
  /**
158
97
  * Returns the Cloud Function that can handle duplicated Storage triggers.
159
- * @type {!CloudFunctionNode8}
98
+ * @type {!CloudFunction}
160
99
  */
161
- const handleFile = (file, context) => {
162
- const eventId = context.eventId;
100
+ const handleFile = async (file, context) => {
101
+ const { eventId } = context;
163
102
  const fileName = file.name;
164
103
  // The second condition prevents the event of folder creation.
165
104
  if (!fileName.startsWith(folder) || folder.startsWith(fileName)) {
166
- const message = `Skip event[${eventId}]: ${fileName} not in the folder: ${
167
- folder}.`;
105
+ const message =
106
+ `Skip event[${eventId}]: ${fileName} not in the folder: ${folder}.`;
168
107
  console.log(message);
169
- return Promise.resolve(message);
170
- } else {
108
+ return message;
109
+ }
110
+ try {
171
111
  const fileObj = new StorageFile(file.bucket, fileName);
172
- return fileObj.getFile()
173
- .move(join(processed, fileName))
174
- .then(([newFile]) => {
175
- console.log(
176
- `Event[${eventId}] move: '${fileName}' to '${newFile.name}'`);
177
- return fn({
178
- name: newFile.name,
179
- oldName: fileName,
180
- bucket: file.bucket,
181
- size: file.size,
182
- updated: file.updated,
183
- }).then(() => {
184
- const message = `Event[${eventId}] completed: ${
185
- newFile.name} triggered the Cloud Functions.`;
186
- console.log(message);
187
- return message;
188
- });
189
- })
190
- .catch((error) => {
191
- let message;
192
- if (error.message.startsWith('file#delete failed with an error')) {
193
- message = `Quit event[${eventId}]: Fail to move ${
194
- fileName}. Maybe duplicated.`;
195
- } else {
196
- message = `Event[${eventId}] triggered: ${
197
- fileName} Cloud Functions got an error: ${error.message}`;
198
- }
199
- console.warn(message);
200
- return message;
201
- });
112
+ const [newFile] = await fileObj.getFile().move(join(processed, fileName));
113
+ console.log(`Event[${eventId}] move: '${fileName}' to '${newFile.name}'`);
114
+ await fn({
115
+ name: newFile.name,
116
+ oldName: fileName,
117
+ bucket: file.bucket,
118
+ size: file.size,
119
+ updated: file.updated,
120
+ });
121
+ const message =
122
+ `Event[${eventId}] completed: ${newFile.name} triggered the Cloud Functions.`;
123
+ console.log(message);
124
+ return message;
125
+ } catch (error) {
126
+ let message;
127
+ if (error.message.startsWith('file#delete failed with an error')) {
128
+ message = `Quit event[${eventId}]: Fail to move ${fileName}. Maybe duplicated.`;
129
+ } else {
130
+ message = `Event[${eventId}] triggered: ${fileName} Cloud Functions got an error: ${error.message}`;
131
+ }
132
+ console.warn(message);
133
+ return message;
202
134
  }
203
135
  };
204
- return adaptNode6(handleFile);
136
+ return handleFile;
205
137
  };
206
138
 
207
139
  /**
@@ -245,10 +177,7 @@ module.exports = {
245
177
  ValidatedStorageFile,
246
178
  MainFunctionOfStorage,
247
179
  EventContext,
248
- CloudFunctionEvent,
249
- CloudFunctionNode8,
250
180
  CloudFunction,
251
- adaptNode6,
252
181
  validatedStorageTrigger,
253
182
  convertEnvPathToAbsolute,
254
183
  };
@@ -153,7 +153,7 @@ class FirestoreAccessBase {
153
153
  * @param {string|number} id Document/Entity Id.
154
154
  * @return {!Promise<(!Entity|undefined)>}
155
155
  */
156
- getObject(id) {
156
+ async getObject(id) {
157
157
  }
158
158
 
159
159
  /**
@@ -163,7 +163,7 @@ class FirestoreAccessBase {
163
163
  * create a new one.
164
164
  * @return {!Promise<string|number>} The ID of saved document/entity.
165
165
  */
166
- saveObject(data, id = undefined) {
166
+ async saveObject(data, id = undefined) {
167
167
  }
168
168
 
169
169
  /**
@@ -171,7 +171,7 @@ class FirestoreAccessBase {
171
171
  * @param {string|number} id Document/Entity Id.
172
172
  * @return {!Promise<boolean>} Whether the operation is succeeded.
173
173
  */
174
- deleteObject(id) {
174
+ async deleteObject(id) {
175
175
  }
176
176
 
177
177
  /**
@@ -187,7 +187,7 @@ class FirestoreAccessBase {
187
187
  * @return {!Promise<!Array<{id:(string|number),entity:!Entity}>>} The
188
188
  * documents or entities.
189
189
  */
190
- queryObjects(filters, order, limit, offset) {
190
+ async queryObjects(filters, order, limit, offset) {
191
191
  }
192
192
 
193
193
  /**
@@ -197,7 +197,7 @@ class FirestoreAccessBase {
197
197
  * invoked with a transaction. It returns any type.
198
198
  * @return {!Promise<*>} The return value of the function which is passed in.
199
199
  */
200
- runTransaction(fn) {
200
+ async runTransaction(fn) {
201
201
  }
202
202
 
203
203
  /**
@@ -207,27 +207,30 @@ class FirestoreAccessBase {
207
207
  * related to a single entity in racing condition. For more complicated use
208
208
  * cases, use `runTransaction` to wrap the function directly.
209
209
  * @param {(string|number)} id Document/Entity Id.
210
- * @param {!TransactionOperation} transactionOperation
210
+ * @param {!TransactionOperation} transactionOperation The operation will be
211
+ * proceeded in a Transaction.
211
212
  * @return {function(!Transaction): Promise<boolean>}
212
213
  */
213
214
  wrapInTransaction(id, transactionOperation) {
214
215
  }
215
216
 
216
- /**
217
- * Returns whether the mode of Firestore is 'Native'.
218
- * @return {!Promise<boolean>}
219
- */
220
- static async isNativeMode(projectId = process.env['GCP_PROJECT']) {
221
- try {
222
- await new Firestore({projectId}).listCollections();
223
- return true;
224
- } catch (error) {
225
- console.log(`In detecting Firestore mode: `, error.message);
226
- return false;
227
- }
228
- };
229
217
  }
230
218
 
219
+ /**
220
+ * Returns whether the mode of Firestore is 'Native'.
221
+ * @param {string=} projectId GCP project Id.
222
+ * @return {!Promise<boolean>}
223
+ */
224
+ async function isNativeMode(projectId = process.env['GCP_PROJECT']) {
225
+ try {
226
+ await new Firestore({ projectId }).listCollections();
227
+ return true;
228
+ } catch (error) {
229
+ console.log(`[No Panic] In detecting Firestore mode: `, error.message);
230
+ return false;
231
+ }
232
+ };
233
+
231
234
  module.exports = {
232
235
  DataSource,
233
236
  Entity,
@@ -237,4 +240,5 @@ module.exports = {
237
240
  DatastoreDocumentFacade,
238
241
  DatastoreTransactionFacade,
239
242
  FirestoreAccessBase,
243
+ isNativeMode,
240
244
  };
@@ -41,14 +41,6 @@ const DatastoreModeAccess = require('./datastore_mode_access.js');
41
41
  */
42
42
  class DataAccessObject {
43
43
 
44
- /**
45
- * Returns the value of environment variable named 'FIRESTORE_TYPE'.
46
- * @return {!DataSource}
47
- */
48
- static getDataSourceFromEnvironment() {
49
- return process.env['FIRESTORE_TYPE'];
50
- }
51
-
52
44
  /**
53
45
  * Initializes the instance based on given data source.
54
46
  * @param {string} kind The data model name.
@@ -56,8 +48,7 @@ class DataAccessObject {
56
48
  * @param {!DataSource} dataSource The data source type.
57
49
  * @param {string} projectId The Id of Cloud project.
58
50
  */
59
- constructor(kind, namespace,
60
- dataSource = DataAccessObject.getDataSourceFromEnvironment(),
51
+ constructor(kind, namespace, dataSource = process.env['FIRESTORE_TYPE'],
61
52
  projectId = process.env['GCP_PROJECT']) {
62
53
  /** @const {string} */ this.namespace = namespace;
63
54
  /** @const {!DataSource} */ this.dataSource = dataSource;
@@ -81,7 +72,7 @@ class DataAccessObject {
81
72
  * @param {!Entity} entity Data to save or update in the document/entity.
82
73
  * @return {!Promise<string|number>} The Id of saved document/entity.
83
74
  */
84
- create(entity) {
75
+ async create(entity) {
85
76
  return this.accessObject.saveObject(entity);
86
77
  }
87
78
 
@@ -92,7 +83,7 @@ class DataAccessObject {
92
83
  * @param {string|number|undefined=} id Document/Entity Id.
93
84
  * @return {!Promise<string|number>} The ID of updated document/entity.
94
85
  */
95
- update(entity, id) {
86
+ async update(entity, id) {
96
87
  return this.accessObject.saveObject(entity, id);
97
88
  }
98
89
 
@@ -103,10 +94,9 @@ class DataAccessObject {
103
94
  * @param {string|number|undefined=} id Document/Entity Id.
104
95
  * @return {!Promise<string|number>} The ID of updated document/entity.
105
96
  */
106
- merge(options, id) {
107
- return this.accessObject.getObject(id).then((entity) => {
108
- return this.update(Object.assign(entity, options), id);
109
- });
97
+ async merge(options, id) {
98
+ const entity = await this.accessObject.getObject(id);
99
+ return this.update(Object.assign(entity, options), id);
110
100
  }
111
101
 
112
102
  /**
@@ -114,7 +104,7 @@ class DataAccessObject {
114
104
  * @param {string|number} id Document/Entity Id.
115
105
  * @return {!Promise<(!Entity|undefined)>}
116
106
  */
117
- load(id) {
107
+ async load(id) {
118
108
  return this.accessObject.getObject(id);
119
109
  }
120
110
 
@@ -123,7 +113,7 @@ class DataAccessObject {
123
113
  * @param {string|number} id Document/Entity Id.
124
114
  * @return {!Promise<boolean>} Whether the operation is succeeded.
125
115
  */
126
- remove(id) {
116
+ async remove(id) {
127
117
  return this.accessObject.deleteObject(id);
128
118
  }
129
119
 
@@ -137,10 +127,10 @@ class DataAccessObject {
137
127
  * https://googleapis.dev/nodejs/datastore/latest/Query.html#order
138
128
  * @param {number|undefined} limit A limit on a query.
139
129
  * @param {number|undefined} offset An offset on a query.
140
- * @return {!Array<{id:(string|number),entity:!Entity}>} The documents or
141
- * entities.
130
+ * @return {!Promise<Array<{id:(string|number),entity:!Entity}>>} The array of
131
+ * documents or entities.
142
132
  */
143
- list(filters, order, limit, offset) {
133
+ async list(filters, order, limit, offset) {
144
134
  return this.accessObject.queryObjects(filters, order, limit, offset);
145
135
  }
146
136
 
@@ -151,7 +141,7 @@ class DataAccessObject {
151
141
  * invoked with a transaction. It returns any type.
152
142
  * @return {!Promise<*>} The return value of the function which is passed in.
153
143
  */
154
- runTransaction(fn) {
144
+ async runTransaction(fn) {
155
145
  return this.accessObject.runTransaction(fn);
156
146
  }
157
147
 
@@ -78,20 +78,19 @@ class DatastoreModeAccess {
78
78
  }
79
79
 
80
80
  /** @override */
81
- getObject(id) {
81
+ async getObject(id) {
82
82
  const key = this.getKey(id);
83
- return this.datastore.get(key)
84
- .then(([entity, error]) => {
85
- if (!error) {
86
- this.logger.debug(`Get ${id}@${this.kind}`, entity);
87
- return entity;
88
- } else {
89
- console.log(`Not found ${id}@${this.kind}`, error);
90
- }
91
- })
92
- .catch((error) => {
93
- console.error(error);
94
- });
83
+ try {
84
+ const [entity] = await this.datastore.get(key);
85
+ if (entity) {
86
+ this.logger.debug(`Get ${id}@${this.kind}`, entity);
87
+ return entity;
88
+ } else {
89
+ this.logger.info(`Not found ${id}@${this.kind}`);
90
+ }
91
+ } catch (error) {
92
+ this.logger.error(error);
93
+ };
95
94
  }
96
95
 
97
96
  /**
@@ -99,52 +98,46 @@ class DatastoreModeAccess {
99
98
  * @param {string|number} id
100
99
  * @return {!Promise<string|number>}
101
100
  */
102
- waitUntilGetObject(id) {
103
- return this.getObject(id).then((entity) => {
104
- if (entity) return id;
105
- this.logger.debug(`Wait 1 more second until the eneity@${id} is ready`);
106
- return wait(1000, this.waitUntilGetObject(id));
107
- });
101
+ async waitUntilGetObject(id) {
102
+ const entity = await this.getObject(id);
103
+ if (entity) return id;
104
+ this.logger.debug(`Wait 1 more second until the eneity@${id} is ready`);
105
+ return wait(1000, this.waitUntilGetObject(id));
108
106
  }
109
107
 
110
108
  /** @override */
111
- saveObject(data, id = undefined) {
109
+ async saveObject(data, id = undefined) {
112
110
  this.logger.debug(`Start to save entity ${id}@${this.kind}`, data);
113
111
  const key = this.getKey(id);
114
- return this.datastore
115
- .save({
116
- key: key,
117
- data: data,
118
- excludeLargeProperties: true,
119
- })
120
- .then((apiResponse) => {
121
- // Default key in Datastore is a number in response like following.
122
- // With a given id, the key in response is null.
123
- const updatedId = id !== undefined ? id
124
- : +apiResponse[0]['mutationResults'][0].key.path[0].id;
125
- this.logger.debug(
126
- `Result of saving ${updatedId}@${this.kind}: `,
127
- JSON.stringify(apiResponse));
128
- // Datastore has a delay to write entity. This method only returns id
129
- // after it is created. For updating, it always return at once because
130
- // the entity exists.
131
- return this.waitUntilGetObject(updatedId);
132
- });
112
+ const apiResponse =
113
+ await this.datastore.save({ key, data, excludeLargeProperties: true });
114
+ // Default key in Datastore is a number in response like following.
115
+ // With a given id, the key in response is null.
116
+ const updatedId = id !== undefined ? id
117
+ : +apiResponse[0].mutationResults[0].key.path[0].id;
118
+ this.logger.debug(`Result of saving ${updatedId}@${this.kind}: `,
119
+ JSON.stringify(apiResponse));
120
+ // Datastore has a delay to write entity. This method only returns id
121
+ // after it is created. For updating, it always return at once because
122
+ // the entity exists.
123
+ return this.waitUntilGetObject(updatedId);
133
124
  }
134
125
 
135
126
  /** @override */
136
- deleteObject(id) {
127
+ async deleteObject(id) {
137
128
  const key = this.getKey(id);
138
- return this.datastore.delete(key).then((apiResponse, error) => {
129
+ try {
130
+ const apiResponse = await this.datastore.delete(key);
139
131
  this.logger.debug(`Delete ${id}@${this.kind}: `,
140
- JSON.stringify(apiResponse));
132
+ JSON.stringify(apiResponse));
141
133
  // Returns true even try to delete a deleted entity. The responses of
142
134
  // delete operations only differ at the property 'indexUpdates' for a
143
135
  // normal entity and a deleted entity.
144
- if (!error) return true;
145
- console.error(`Error in deleting ${id}@${this.kind}`, error);
136
+ return true;
137
+ } catch (error) {
138
+ this.logger.error(`Error in deleting ${id}@${this.kind}`, error);
146
139
  return false;
147
- });
140
+ }
148
141
  }
149
142
 
150
143
  /** @override */
@@ -164,7 +157,7 @@ class DatastoreModeAccess {
164
157
  this.datastore.runQueryStream(query)
165
158
  .on('error',
166
159
  (error) => {
167
- console.error(error);
160
+ this.logger.error(error);
168
161
  reject(error);
169
162
  })
170
163
  .on('data',
@@ -174,7 +167,7 @@ class DatastoreModeAccess {
174
167
  })
175
168
  .on('info',
176
169
  (info) => {
177
- console.log(`Info event: ${JSON.stringify(info)}`);
170
+ this.logger.info(`Info event: ${JSON.stringify(info)}`);
178
171
  })
179
172
  .on('end', () => {
180
173
  resolve(result);
@@ -190,35 +183,36 @@ class DatastoreModeAccess {
190
183
  *
191
184
  * @override
192
185
  */
193
- runTransaction(fn) {
186
+ async runTransaction(fn) {
194
187
  let leftRetries = MAX_RETRY_TIMES;
195
- const runFn = () => this.datastore.transaction().run().then(
196
- ([transaction]) => fn(transaction));
197
- const retryFn = (error) => {
198
- console.log(
188
+ const runFn = async () => {
189
+ const [transaction] = await this.datastore.transaction().run();
190
+ return fn(transaction);
191
+ };
192
+ const retryFn = async (error) => {
193
+ this.logger.info(
199
194
  `TX ERROR[${error.message}]. Retries left: ${leftRetries} times.`);
200
195
  leftRetries--;
201
- return wait(500 * (MAX_RETRY_TIMES - leftRetries)).then(() => {
202
- if (leftRetries === 0) return runFn();
203
- return runFn().catch(retryFn);
204
- });
196
+ await wait(500 * (MAX_RETRY_TIMES - leftRetries));
197
+ if (leftRetries === 0) return runFn();
198
+ return runFn().catch(retryFn);
205
199
  };
206
200
  return runFn().catch(retryFn);
207
201
  }
208
202
 
209
203
  /** @override */
210
204
  wrapInTransaction(id, transactionOperation) {
211
- return (transaction) => {
212
- console.log(`Transaction starts for ${this.kind}@${id}.`);
205
+ return async (transaction) => {
206
+ this.logger.info(`Transaction starts for ${this.kind}@${id}.`);
213
207
  const key = this.getKey(id);
214
- return transaction.get(key).then(([entity]) => {
215
- const result = transactionOperation(
216
- new DatastoreDocumentFacade(entity),
217
- key,
218
- new DatastoreTransactionFacade(transaction, entity)
219
- );
220
- return transaction.commit().then(() => result);
221
- });
208
+ const [entity] = await transaction.get(key);
209
+ const result = transactionOperation(
210
+ new DatastoreDocumentFacade(entity),
211
+ key,
212
+ new DatastoreTransactionFacade(transaction, entity)
213
+ );
214
+ await transaction.commit();
215
+ return result;
222
216
  };
223
217
  }
224
218
  }