@google-cloud/nodejs-common 1.8.0 → 1.8.2-beta
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/bin/install_functions.sh +2 -2
- package/package.json +4 -3
- package/src/apis/ads_data_hub.js +50 -87
- package/src/apis/base/auth_restful_api.js +59 -0
- package/src/apis/base/restful_api_base.js +143 -0
- package/src/apis/doubleclick_bidmanager.js +1 -1
- package/src/apis/index.js +16 -1
- package/src/apis/measurement_protocol.js +1 -0
- package/src/apis/sendgrid.js +65 -0
- package/src/components/cloudfunctions_utils.js +32 -103
- package/src/components/firestore/access_base.js +23 -19
- package/src/components/firestore/data_access_object.js +12 -22
- package/src/components/firestore/datastore_mode_access.js +60 -66
- package/src/components/firestore/native_mode_access.js +48 -56
- package/src/components/storage.js +44 -70
- package/src/components/utils.js +5 -7
|
@@ -66,75 +66,14 @@ let MainFunctionOfStorage;
|
|
|
66
66
|
let EventContext;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
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 {!
|
|
98
|
+
* @type {!CloudFunction}
|
|
160
99
|
*/
|
|
161
|
-
const handleFile = (file, context) => {
|
|
162
|
-
const eventId = context
|
|
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 =
|
|
167
|
-
|
|
105
|
+
const message =
|
|
106
|
+
`Skip event[${eventId}]: ${fileName} not in the folder: ${folder}.`;
|
|
168
107
|
console.log(message);
|
|
169
|
-
return
|
|
170
|
-
}
|
|
108
|
+
return message;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
171
111
|
const fileObj = new StorageFile(file.bucket, fileName);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
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}
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
129
|
+
try {
|
|
130
|
+
const apiResponse = await this.datastore.delete(key);
|
|
139
131
|
this.logger.debug(`Delete ${id}@${this.kind}: `,
|
|
140
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = () =>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
205
|
+
return async (transaction) => {
|
|
206
|
+
this.logger.info(`Transaction starts for ${this.kind}@${id}.`);
|
|
213
207
|
const key = this.getKey(id);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
}
|