forked from canada-ca/tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransaction.js
More file actions
325 lines (325 loc) · 11 KB
/
Copy pathtransaction.js
File metadata and controls
325 lines (325 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Transaction = exports.isArangoTransaction = void 0;
const error_1 = require("./error");
const codes_1 = require("./lib/codes");
/**
* Indicates whether the given value represents a {@link Transaction}.
*
* @param transaction - A value that might be a transaction.
*/
function isArangoTransaction(transaction) {
return Boolean(transaction && transaction.isArangoTransaction);
}
exports.isArangoTransaction = isArangoTransaction;
/**
* Represents a streaming transaction in a {@link Database}.
*/
class Transaction {
/**
* @internal
* @hidden
*/
constructor(db, id) {
this._db = db;
this._id = id;
}
/**
* @internal
*
* Indicates that this object represents an ArangoDB transaction.
*/
get isArangoTransaction() {
return true;
}
/**
* Unique identifier of this transaction.
*
* See {@link Database.transaction}.
*/
get id() {
return this._id;
}
/**
* Checks whether the transaction exists.
*
* @example
* ```js
* const db = new Database();
* const trx = db.transaction("some-transaction");
* const result = await trx.exists();
* // result indicates whether the transaction exists
* ```
*/
async exists() {
try {
await this.get();
return true;
}
catch (err) {
if (error_1.isArangoError(err) && err.errorNum === codes_1.TRANSACTION_NOT_FOUND) {
return false;
}
throw err;
}
}
/**
* Retrieves general information about the transaction.
*
* @example
* ```js
* const db = new Database();
* const col = db.collection("some-collection");
* const trx = db.beginTransaction(col);
* await trx.step(() => col.save({ hello: "world" }));
* const info = await trx.get();
* // the transaction exists
* ```
*/
get() {
return this._db.request({
path: `/_api/transaction/${this.id}`,
}, (res) => res.body.result);
}
/**
* Attempts to commit the transaction to the databases.
*
* @example
* ```js
* const db = new Database();
* const col = db.collection("some-collection");
* const trx = db.beginTransaction(col);
* await trx.step(() => col.save({ hello: "world" }));
* const result = await trx.commit();
* // result indicates the updated transaction status
* ```
*/
commit() {
return this._db.request({
method: "PUT",
path: `/_api/transaction/${this.id}`,
}, (res) => res.body.result);
}
/**
* Attempts to abort the transaction to the databases.
*
* @example
* ```js
* const db = new Database();
* const col = db.collection("some-collection");
* const trx = db.beginTransaction(col);
* await trx.step(() => col.save({ hello: "world" }));
* const result = await trx.abort();
* // result indicates the updated transaction status
* ```
*/
abort() {
return this._db.request({
method: "DELETE",
path: `/_api/transaction/${this.id}`,
}, (res) => res.body.result);
}
/**
* Executes the given function locally as a single step of the transaction.
*
* @param T - Type of the callback's returned promise.
* @param callback - Callback function returning a promise.
*
* **Warning**: The callback function should wrap a single call of an async
* arangojs method (e.g. a method on a `Collection` object of a collection
* that is involved in the transaction or the `db.query` method).
* If the callback function is async, only the first promise-returning (or
* async) method call will be executed as part of the transaction. See the
* examples below for how to avoid common mistakes when using this method.
*
* **Note**: Avoid defining the callback as an async function if possible
* as arangojs will throw an error if the callback did not return a promise.
* Async functions will return an empty promise by default, making it harder
* to notice if you forgot to return something from the callback.
*
* **Note**: Although almost anything can be wrapped in a callback and passed
* to this method, that does not guarantee ArangoDB can actually do it in a
* transaction. Refer to the ArangoDB documentation if you are unsure whether
* a given operation can be executed as part of a transaction. Generally any
* modification or retrieval of data is eligible but modifications of
* collections or databases are not.
*
* @example
* ```js
* const db = new Database();
* const vertices = db.collection("vertices");
* const edges = db.collection("edges");
* const trx = await db.beginTransaction({ write: [vertices, edges] });
*
* // The following code will be part of the transaction
* const left = await trx.step(() => vertices.save({ label: "left" }));
* const right = await trx.step(() => vertices.save({ label: "right" }));
*
* // Results from preceding actions can be used normally
* await trx.step(() => edges.save({
* _from: left._id,
* _to: right._id,
* data: "potato"
* }));
*
* // Transaction must be committed for changes to take effected
* // Always call either trx.commit or trx.abort to end a transaction
* await trx.commit();
* ```
*
* @example
* ```js
* // BAD! If the callback is an async function it must only use await once!
* await trx.step(async () => {
* await collection.save(data);
* await collection.save(moreData); // WRONG
* });
*
* // BAD! Callback function must use only one arangojs call!
* await trx.step(() => {
* return collection.save(data)
* .then(() => collection.save(moreData)); // WRONG
* });
*
* // BETTER: Wrap every arangojs method call that should be part of the
* // transaction in a separate `trx.step` call
* await trx.step(() => collection.save(data));
* await trx.step(() => collection.save(moreData));
* ```
*
* @example
* ```js
* // BAD! If the callback is an async function it must not await before
* // calling an arangojs method!
* await trx.step(async () => {
* await doSomethingElse();
* return collection.save(data); // WRONG
* });
*
* // BAD! Any arangojs inside the callback must not happen inside a promise
* // method!
* await trx.step(() => {
* return doSomethingElse()
* .then(() => collection.save(data)); // WRONG
* });
*
* // BETTER: Perform any async logic needed outside the `trx.step` call
* await doSomethingElse();
* await trx.step(() => collection.save(data));
*
* // OKAY: You can perform async logic in the callback after the arangojs
* // method call as long as it does not involve additional arangojs method
* // calls, but this makes it easy to make mistakes later
* await trx.step(async () => {
* await collection.save(data);
* await doSomethingDifferent(); // no arangojs method calls allowed
* });
* ```
*
* @example
* ```js
* // BAD! The callback should not use any functions that themselves use any
* // arangojs methods!
* async function saveSomeData() {
* await collection.save(data);
* await collection.save(moreData);
* }
* await trx.step(() => saveSomeData()); // WRONG
*
* // BETTER: Pass the transaction to functions that need to call arangojs
* // methods inside a transaction
* async function saveSomeData(trx) {
* await trx.step(() => collection.save(data));
* await trx.step(() => collection.save(moreData));
* }
* await saveSomeData(); // no `trx.step` call needed
* ```
*
* @example
* ```js
* // BAD! You must wait for the promise to resolve (or await on the
* // `trx.step` call) before calling `trx.step` again!
* trx.step(() => collection.save(data)); // WRONG
* await trx.step(() => collection.save(moreData));
*
* // BAD! The trx.step callback can not make multiple calls to async arangojs
* // methods, not even using Promise.all!
* await trx.step(() => Promise.all([ // WRONG
* collection.save(data),
* collection.save(moreData),
* ]));
*
* // BAD! Multiple `trx.step` calls can not run in parallel!
* await Promise.all([ // WRONG
* trx.step(() => collection.save(data)),
* trx.step(() => collection.save(moreData)),
* ]));
*
* // BETTER: Always call `trx.step` sequentially, one after the other
* await trx.step(() => collection.save(data));
* await trx.step(() => collection.save(moreData));
*
* // OKAY: The then callback can be used if async/await is not available
* trx.step(() => collection.save(data))
* .then(() => trx.step(() => collection.save(moreData)));
* ```
*
* @example
* ```js
* // BAD! The callback will return an empty promise that resolves before
* // the inner arangojs method call has even talked to ArangoDB!
* await trx.step(async () => {
* collection.save(data); // WRONG
* });
*
* // BETTER: Use an arrow function so you don't forget to return
* await trx.step(() => collection.save(data));
*
* // OKAY: Remember to always return when using a function body
* await trx.step(() => {
* return collection.save(data); // easy to forget!
* });
*
* // OKAY: You do not have to use arrow functions but it helps
* await trx.step(function () {
* return collection.save(data);
* });
* ```
*
* @example
* ```js
* // BAD! You can not pass promises instead of a callback!
* await trx.step(collection.save(data)); // WRONG
*
* // BETTER: Wrap the code in a function and pass the function instead
* await trx.step(() => collection.save(data));
* ```
*
* @example
* ```js
* // WORSE: Calls to non-async arangojs methods don't need to be performed
* // as part of a transaction
* const collection = await trx.step(() => db.collection("my-documents"));
*
* // BETTER: If an arangojs method is not async and doesn't return promises,
* // call it without `trx.step`
* const collection = db.collection("my-documents");
* ```
*/
step(callback) {
const conn = this._db._connection;
conn.setTransactionId(this.id);
try {
const promise = callback();
if (!promise) {
throw new Error("Transaction callback was not an async function or did not return a promise!");
}
return Promise.resolve(promise);
}
finally {
conn.clearTransactionId();
}
}
}
exports.Transaction = Transaction;
//# sourceMappingURL=transaction.js.map