arangojs
Version:
The official ArangoDB JavaScript driver.
1 lines • 15.3 kB
Source Map (JSON)
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../src/transaction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ```ts\n * import type { Transaction } from \"arangojs/transaction.js\";\n * ```\n *\n * The \"transaction\" module provides transaction related types and interfaces\n * for TypeScript.\n *\n * @packageDocumentation\n */\nimport { Connection } from \"./connection.js\";\nimport { Database } from \"./database.js\";\nimport { isArangoError } from \"./error.js\";\nimport { TRANSACTION_NOT_FOUND } from \"./lib/codes.js\";\n\n/**\n * Indicates whether the given value represents a {@link Transaction}.\n *\n * @param transaction - A value that might be a transaction.\n */\nexport function isArangoTransaction(\n transaction: any\n): transaction is Transaction {\n return Boolean(transaction && transaction.isArangoTransaction);\n}\n\n/**\n * Options for how the transaction should be committed.\n */\nexport type TransactionCommitOptions = {\n /**\n * If set to `true`, the request will explicitly permit ArangoDB to return a\n * potentially dirty or stale result and arangojs will load balance the\n * request without distinguishing between leaders and followers.\n */\n allowDirtyRead?: boolean;\n};\n\n/**\n * Options for how the transaction should be aborted.\n */\nexport type TransactionAbortOptions = {\n /**\n * If set to `true`, the request will explicitly permit ArangoDB to return a\n * potentially dirty or stale result and arangojs will load balance the\n * request without distinguishing between leaders and followers.\n */\n allowDirtyRead?: boolean;\n};\n\n/**\n * Status of a given transaction.\n *\n * See also {@link database.TransactionDetails}.\n */\nexport type TransactionStatus = {\n /**\n * Unique identifier of the transaction.\n */\n id: string;\n /**\n * Status of the transaction.\n */\n status: \"running\" | \"committed\" | \"aborted\";\n};\n\n/**\n * Represents a streaming transaction in a {@link database.Database}.\n */\nexport class Transaction {\n protected _db: Database;\n protected _id: string;\n\n /**\n * @internal\n */\n constructor(db: Database, id: string) {\n this._db = db;\n this._id = id;\n }\n\n /**\n * @internal\n *\n * Indicates that this object represents an ArangoDB transaction.\n */\n get isArangoTransaction(): true {\n return true;\n }\n\n /**\n * Unique identifier of this transaction.\n *\n * See {@link database.Database#transaction}.\n */\n get id() {\n return this._id;\n }\n\n /**\n * Checks whether the transaction exists.\n *\n * @example\n * ```js\n * const db = new Database();\n * const trx = db.transaction(\"some-transaction\");\n * const result = await trx.exists();\n * // result indicates whether the transaction exists\n * ```\n */\n async exists(): Promise<boolean> {\n try {\n await this.get();\n return true;\n } catch (err: any) {\n if (isArangoError(err) && err.errorNum === TRANSACTION_NOT_FOUND) {\n return false;\n }\n throw err;\n }\n }\n\n /**\n * Retrieves general information about the transaction.\n *\n * @example\n * ```js\n * const db = new Database();\n * const col = db.collection(\"some-collection\");\n * const trx = db.beginTransaction(col);\n * await trx.step(() => col.save({ hello: \"world\" }));\n * const info = await trx.get();\n * // the transaction exists\n * ```\n */\n get(): Promise<TransactionStatus> {\n return this._db.request(\n {\n path: `/_api/transaction/${encodeURIComponent(this.id)}`,\n },\n (res) => res.parsedBody.result\n );\n }\n\n /**\n * Attempts to commit the transaction to the databases.\n *\n * @param options - Options for comitting the transaction.\n *\n * @example\n * ```js\n * const db = new Database();\n * const col = db.collection(\"some-collection\");\n * const trx = db.beginTransaction(col);\n * await trx.step(() => col.save({ hello: \"world\" }));\n * const result = await trx.commit();\n * // result indicates the updated transaction status\n * ```\n */\n commit(options: TransactionCommitOptions = {}): Promise<TransactionStatus> {\n const { allowDirtyRead = undefined } = options;\n return this._db.request(\n {\n method: \"PUT\",\n path: `/_api/transaction/${encodeURIComponent(this.id)}`,\n allowDirtyRead,\n },\n (res) => res.parsedBody.result\n );\n }\n\n /**\n * Attempts to abort the transaction to the databases.\n *\n * @param options - Options for aborting the transaction.\n *\n * @example\n * ```js\n * const db = new Database();\n * const col = db.collection(\"some-collection\");\n * const trx = db.beginTransaction(col);\n * await trx.step(() => col.save({ hello: \"world\" }));\n * const result = await trx.abort();\n * // result indicates the updated transaction status\n * ```\n */\n abort(options: TransactionAbortOptions = {}): Promise<TransactionStatus> {\n const { allowDirtyRead = undefined } = options;\n return this._db.request(\n {\n method: \"DELETE\",\n path: `/_api/transaction/${encodeURIComponent(this.id)}`,\n allowDirtyRead,\n },\n (res) => res.parsedBody.result\n );\n }\n\n /**\n * Executes the given function locally as a single step of the transaction.\n *\n * @param T - Type of the callback's returned promise.\n * @param callback - Callback function returning a promise.\n *\n * **Warning**: The callback function should wrap a single call of an async\n * arangojs method (e.g. a method on a `Collection` object of a collection\n * that is involved in the transaction or the `db.query` method).\n * If the callback function is async, only the first promise-returning (or\n * async) method call will be executed as part of the transaction. See the\n * examples below for how to avoid common mistakes when using this method.\n *\n * **Note**: Avoid defining the callback as an async function if possible\n * as arangojs will throw an error if the callback did not return a promise.\n * Async functions will return an empty promise by default, making it harder\n * to notice if you forgot to return something from the callback.\n *\n * **Note**: Although almost anything can be wrapped in a callback and passed\n * to this method, that does not guarantee ArangoDB can actually do it in a\n * transaction. Refer to the ArangoDB documentation if you are unsure whether\n * a given operation can be executed as part of a transaction. Generally any\n * modification or retrieval of data is eligible but modifications of\n * collections or databases are not.\n *\n * @example\n * ```js\n * const db = new Database();\n * const vertices = db.collection(\"vertices\");\n * const edges = db.collection(\"edges\");\n * const trx = await db.beginTransaction({ write: [vertices, edges] });\n *\n * // The following code will be part of the transaction\n * const left = await trx.step(() => vertices.save({ label: \"left\" }));\n * const right = await trx.step(() => vertices.save({ label: \"right\" }));\n *\n * // Results from preceding actions can be used normally\n * await trx.step(() => edges.save({\n * _from: left._id,\n * _to: right._id,\n * data: \"potato\"\n * }));\n *\n * // Transaction must be committed for changes to take effected\n * // Always call either trx.commit or trx.abort to end a transaction\n * await trx.commit();\n * ```\n *\n * @example\n * ```js\n * // BAD! If the callback is an async function it must only use await once!\n * await trx.step(async () => {\n * await collection.save(data);\n * await collection.save(moreData); // WRONG\n * });\n *\n * // BAD! Callback function must use only one arangojs call!\n * await trx.step(() => {\n * return collection.save(data)\n * .then(() => collection.save(moreData)); // WRONG\n * });\n *\n * // BETTER: Wrap every arangojs method call that should be part of the\n * // transaction in a separate `trx.step` call\n * await trx.step(() => collection.save(data));\n * await trx.step(() => collection.save(moreData));\n * ```\n *\n * @example\n * ```js\n * // BAD! If the callback is an async function it must not await before\n * // calling an arangojs method!\n * await trx.step(async () => {\n * await doSomethingElse();\n * return collection.save(data); // WRONG\n * });\n *\n * // BAD! Any arangojs inside the callback must not happen inside a promise\n * // method!\n * await trx.step(() => {\n * return doSomethingElse()\n * .then(() => collection.save(data)); // WRONG\n * });\n *\n * // BETTER: Perform any async logic needed outside the `trx.step` call\n * await doSomethingElse();\n * await trx.step(() => collection.save(data));\n *\n * // OKAY: You can perform async logic in the callback after the arangojs\n * // method call as long as it does not involve additional arangojs method\n * // calls, but this makes it easy to make mistakes later\n * await trx.step(async () => {\n * await collection.save(data);\n * await doSomethingDifferent(); // no arangojs method calls allowed\n * });\n * ```\n *\n * @example\n * ```js\n * // BAD! The callback should not use any functions that themselves use any\n * // arangojs methods!\n * async function saveSomeData() {\n * await collection.save(data);\n * await collection.save(moreData);\n * }\n * await trx.step(() => saveSomeData()); // WRONG\n *\n * // BETTER: Pass the transaction to functions that need to call arangojs\n * // methods inside a transaction\n * async function saveSomeData(trx) {\n * await trx.step(() => collection.save(data));\n * await trx.step(() => collection.save(moreData));\n * }\n * await saveSomeData(); // no `trx.step` call needed\n * ```\n *\n * @example\n * ```js\n * // BAD! You must wait for the promise to resolve (or await on the\n * // `trx.step` call) before calling `trx.step` again!\n * trx.step(() => collection.save(data)); // WRONG\n * await trx.step(() => collection.save(moreData));\n *\n * // BAD! The trx.step callback can not make multiple calls to async arangojs\n * // methods, not even using Promise.all!\n * await trx.step(() => Promise.all([ // WRONG\n * collection.save(data),\n * collection.save(moreData),\n * ]));\n *\n * // BAD! Multiple `trx.step` calls can not run in parallel!\n * await Promise.all([ // WRONG\n * trx.step(() => collection.save(data)),\n * trx.step(() => collection.save(moreData)),\n * ]));\n *\n * // BETTER: Always call `trx.step` sequentially, one after the other\n * await trx.step(() => collection.save(data));\n * await trx.step(() => collection.save(moreData));\n *\n * // OKAY: The then callback can be used if async/await is not available\n * trx.step(() => collection.save(data))\n * .then(() => trx.step(() => collection.save(moreData)));\n * ```\n *\n * @example\n * ```js\n * // BAD! The callback will return an empty promise that resolves before\n * // the inner arangojs method call has even talked to ArangoDB!\n * await trx.step(async () => {\n * collection.save(data); // WRONG\n * });\n *\n * // BETTER: Use an arrow function so you don't forget to return\n * await trx.step(() => collection.save(data));\n *\n * // OKAY: Remember to always return when using a function body\n * await trx.step(() => {\n * return collection.save(data); // easy to forget!\n * });\n *\n * // OKAY: You do not have to use arrow functions but it helps\n * await trx.step(function () {\n * return collection.save(data);\n * });\n * ```\n *\n * @example\n * ```js\n * // BAD! You can not pass promises instead of a callback!\n * await trx.step(collection.save(data)); // WRONG\n *\n * // BETTER: Wrap the code in a function and pass the function instead\n * await trx.step(() => collection.save(data));\n * ```\n *\n * @example\n * ```js\n * // WORSE: Calls to non-async arangojs methods don't need to be performed\n * // as part of a transaction\n * const collection = await trx.step(() => db.collection(\"my-documents\"));\n *\n * // BETTER: If an arangojs method is not async and doesn't return promises,\n * // call it without `trx.step`\n * const collection = db.collection(\"my-documents\");\n * ```\n */\n step<T>(callback: () => Promise<T>): Promise<T> {\n const conn = (this._db as any)._connection as Connection;\n conn.setTransactionId(this.id);\n try {\n const promise = callback();\n if (!promise) {\n throw new Error(\n \"Transaction callback was not an async function or did not return a promise!\"\n );\n }\n return Promise.resolve(promise);\n } finally {\n conn.clearTransactionId();\n }\n }\n}\n"]}