UNPKG

arangojs

Version:

The official ArangoDB JavaScript driver.

1,524 lines (1,523 loc) 78.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Database = exports.LogLevel = exports.isArangoDatabase = void 0; /** * ```js * import { Database } from "arangojs/database.js"; * ``` * * The "database" module provides the {@link Database} class and associated * types and interfaces for TypeScript. * * The Database class is also re-exported by the "index" module. * * @packageDocumentation */ const analyzer_js_1 = require("./analyzer.js"); const aql_js_1 = require("./aql.js"); const collection_js_1 = require("./collection.js"); const connection_js_1 = require("./connection.js"); const cursor_js_1 = require("./cursor.js"); const error_js_1 = require("./error.js"); const graph_js_1 = require("./graph.js"); const job_js_1 = require("./job.js"); const codes_js_1 = require("./lib/codes.js"); const route_js_1 = require("./route.js"); const transaction_js_1 = require("./transaction.js"); const view_js_1 = require("./view.js"); /** * Indicates whether the given value represents a {@link Database}. * * @param database - A value that might be a database. */ function isArangoDatabase(database) { return Boolean(database && database.isArangoDatabase); } exports.isArangoDatabase = isArangoDatabase; /** * @internal */ function coerceTransactionCollections(collections) { if (typeof collections === "string") { return { write: [collections] }; } if (Array.isArray(collections)) { return { write: collections.map(collection_js_1.collectionToString) }; } if ((0, collection_js_1.isArangoCollection)(collections)) { return { write: (0, collection_js_1.collectionToString)(collections) }; } const cols = {}; if (collections) { if (collections.allowImplicit !== undefined) { cols.allowImplicit = collections.allowImplicit; } if (collections.read) { cols.read = Array.isArray(collections.read) ? collections.read.map(collection_js_1.collectionToString) : (0, collection_js_1.collectionToString)(collections.read); } if (collections.write) { cols.write = Array.isArray(collections.write) ? collections.write.map(collection_js_1.collectionToString) : (0, collection_js_1.collectionToString)(collections.write); } if (collections.exclusive) { cols.exclusive = Array.isArray(collections.exclusive) ? collections.exclusive.map(collection_js_1.collectionToString) : (0, collection_js_1.collectionToString)(collections.exclusive); } } return cols; } /** * Numeric representation of the logging level of a log entry. */ var LogLevel; (function (LogLevel) { LogLevel[LogLevel["FATAL"] = 0] = "FATAL"; LogLevel[LogLevel["ERROR"] = 1] = "ERROR"; LogLevel[LogLevel["WARNING"] = 2] = "WARNING"; LogLevel[LogLevel["INFO"] = 3] = "INFO"; LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG"; })(LogLevel || (exports.LogLevel = LogLevel = {})); /** * An object representing a single ArangoDB database. All arangojs collections, * cursors, analyzers and so on are linked to a `Database` object. */ class Database { _connection; _name; _analyzers = new Map(); _collections = new Map(); _graphs = new Map(); _views = new Map(); _trapRequest; constructor(configOrDatabase = {}, name) { if (isArangoDatabase(configOrDatabase)) { const connection = configOrDatabase._connection; const databaseName = name || configOrDatabase.name; this._connection = connection; this._name = databaseName; const database = connection.database(databaseName); if (database) return database; } else { const config = configOrDatabase; const { databaseName, ...options } = typeof config === "string" || Array.isArray(config) ? { databaseName: name, url: config } : config; this._connection = new connection_js_1.Connection(options); this._name = databaseName || "_system"; } } //#region misc /** * @internal * * Indicates that this object represents an ArangoDB database. */ get isArangoDatabase() { return true; } /** * Name of the ArangoDB database this instance represents. */ get name() { return this._name; } /** * Fetches version information from the ArangoDB server. * * @param details - If set to `true`, additional information about the * ArangoDB server will be available as the `details` property. * * @example * ```js * const db = new Database(); * const version = await db.version(); * // the version object contains the ArangoDB version information. * // license: "community" or "enterprise" * // version: ArangoDB version number * // server: description of the server * ``` */ version(details) { return this.request({ method: "GET", path: "/_api/version", search: { details }, }); } /** * Retrives the server's current system time in milliseconds with microsecond * precision. */ time() { return this.request({ path: "/_admin/time", }, (res) => res.parsedBody.time * 1000); } /** * Returns a new {@link route.Route} instance for the given path (relative to the * database) that can be used to perform arbitrary HTTP requests. * * @param path - The database-relative URL of the route. Defaults to the * database API root. * @param headers - Default headers that should be sent with each request to * the route. * * @example * ```js * const db = new Database(); * const myFoxxService = db.route("my-foxx-service"); * const response = await myFoxxService.post("users", { * username: "admin", * password: "hunter2" * }); * // response.body is the result of * // POST /_db/_system/my-foxx-service/users * // with JSON request body '{"username": "admin", "password": "hunter2"}' * ``` */ route(path, headers) { return new route_js_1.Route(this, path, headers); } /** * Creates an async job by executing the given callback function. The first * database request performed by the callback will be marked for asynchronous * execution and its result will be made available as an async job. * * Returns a {@link Job} instance that can be used to retrieve the result * of the callback function once the request has been executed. * * @param callback - Callback function to execute as an async job. * * @example * ```js * const db = new Database(); * const job = await db.createJob(() => db.collections()); * while (!job.isLoaded) { * await timeout(1000); * await job.load(); * } * // job.result is a list of Collection instances * ``` */ async createJob(callback) { const trap = new Promise((resolveTrap) => { this._trapRequest = (trapped) => resolveTrap(trapped); }); const eventualResult = callback(); const trapped = await trap; if (trapped.error) return eventualResult; const { jobId, onResolve, onReject } = trapped; return new job_js_1.Job(this, jobId, (res) => { onResolve(res); return eventualResult; }, (e) => { onReject(e); return eventualResult; }); } async request({ absolutePath = false, basePath, ...opts }, transform = (res) => res.parsedBody) { if (!absolutePath) { basePath = `/_db/${encodeURIComponent(this._name)}${basePath || ""}`; } if (this._trapRequest) { const trap = this._trapRequest; this._trapRequest = undefined; return new Promise(async (resolveRequest, rejectRequest) => { const options = { ...opts }; options.headers = new Headers(options.headers); options.headers.set("x-arango-async", "store"); let jobRes; try { jobRes = await this._connection.request({ basePath, ...options }); } catch (e) { trap({ error: true }); rejectRequest(e); return; } const jobId = jobRes.headers.get("x-arango-async-id"); trap({ jobId, onResolve: (res) => { const result = transform ? transform(res) : res; resolveRequest(result); return result; }, onReject: (err) => { rejectRequest(err); throw err; }, }); }); } return this._connection.request({ basePath, ...opts }, transform || undefined); } /** * Updates the URL list by requesting a list of all coordinators in the * cluster and adding any endpoints not initially specified in the * {@link connection.Config}. * * For long-running processes communicating with an ArangoDB cluster it is * recommended to run this method periodically (e.g. once per hour) to make * sure new coordinators are picked up correctly and can be used for * fail-over or load balancing. * * @param overwrite - If set to `true`, the existing host list will be * replaced instead of extended. * * @example * ```js * const db = new Database(); * const interval = setInterval( * () => db.acquireHostList(), * 5 * 60 * 1000 // every 5 minutes * ); * * // later * clearInterval(interval); * system.close(); * ``` */ async acquireHostList(overwrite = false) { const urls = await this.request({ path: "/_api/cluster/endpoints" }, (res) => res.parsedBody.endpoints.map((endpoint) => endpoint.endpoint)); if (urls.length > 0) { if (overwrite) this._connection.setHostList(urls); else this._connection.addToHostList(urls); } } /** * Closes all active connections of this database instance. * * Can be used to clean up idling connections during longer periods of * inactivity. * * **Note**: This method currently has no effect in the browser version of * arangojs. * * @example * ```js * const db = new Database(); * const sessions = db.collection("sessions"); * // Clean up expired sessions once per hour * setInterval(async () => { * await db.query(aql` * FOR session IN ${sessions} * FILTER session.expires < DATE_NOW() * REMOVE session IN ${sessions} * `); * // Making sure to close the connections because they're no longer used * system.close(); * }, 1000 * 60 * 60); * ``` */ close() { this._connection.close(); } /** * Attempts to initiate a clean shutdown of the server. */ shutdown() { return this.request({ method: "DELETE", path: "/_admin/shutdown", }, () => undefined); } async waitForPropagation({ basePath, ...request }, timeout) { await this._connection.waitForPropagation({ ...request, basePath: `/_db/${encodeURIComponent(this._name)}${basePath || ""}`, }, timeout); } /** * Methods for accessing the server-reported queue times of the mostly * recently received responses. */ get queueTime() { return this._connection.queueTime; } /** * Sets the limit for the number of values of the most recently received * server-reported queue times that can be accessed using * {@link Database#queueTime}. * * @param responseQueueTimeSamples - Number of values to maintain. */ setResponseQueueTimeSamples(responseQueueTimeSamples) { this._connection.setResponseQueueTimeSamples(responseQueueTimeSamples); } //#endregion //#region auth /** * Updates the underlying connection's `authorization` header to use Basic * authentication with the given `username` and `password`, then returns * itself. * * @param username - The username to authenticate with. * @param password - The password to authenticate with. * * @example * ```js * const db = new Database(); * db.useBasicAuth("admin", "hunter2"); * // with the username "admin" and password "hunter2". * ``` */ useBasicAuth(username = "root", password = "") { this._connection.setBasicAuth({ username, password }); return this; } /** * Updates the underlying connection's `authorization` header to use Bearer * authentication with the given authentication `token`, then returns itself. * * @param token - The token to authenticate with. * * @example * ```js * const db = new Database(); * db.useBearerAuth("keyboardcat"); * // The database instance now uses Bearer authentication. * ``` */ useBearerAuth(token) { this._connection.setBearerAuth({ token }); return this; } /** * Validates the given database credentials and exchanges them for an * authentication token, then uses the authentication token for future * requests and returns it. * * @param username - The username to authenticate with. * @param password - The password to authenticate with. * * @example * ```js * const db = new Database(); * await db.login("admin", "hunter2"); * // with an authentication token for the "admin" user. * ``` */ login(username = "root", password = "") { return this.request({ method: "POST", path: "/_open/auth", body: { username, password }, }, (res) => { this.useBearerAuth(res.parsedBody.jwt); return res.parsedBody.jwt; }); } /** * Attempts to renew the authentication token passed to {@link Database#useBearerAuth} * or returned and used by {@link Database#login}. If a new authentication * token is issued, it will be used for future requests and returned. * * @example * ```js * const db = new Database(); * await db.login("admin", "hunter2"); * // ... later ... * const newToken = await db.renewAuthToken(); * if (!newToken) // no new token issued * ``` */ renewAuthToken() { return this.request({ method: "POST", path: "/_open/auth/renew", }, (res) => { if (!res.parsedBody.jwt) return null; this.useBearerAuth(res.parsedBody.jwt); return res.parsedBody.jwt; }); } //#endregion //#region rebalancing /** * Computes the current cluster imbalance. * * @example * ```js * const db = new Database(); * const imbalance = await db.getClusterImbalance(); * ``` */ getClusterImbalance() { return this.request({ path: "/_admin/cluster/rebalance" }, (res) => res.parsedBody.result); } /** * Computes a set of move shard operations to rebalance the cluster. * * @example * ```js * const db = new Database(); * const result = await db.computerClusterRebalance({ * moveLeaders: true, * moveFollowers: true * }); * if (result.moves.length) { * await db.executeClusterRebalance(result.moves); * } * ``` */ computeClusterRebalance(opts) { return this.request({ method: "POST", path: "/_admin/cluster/rebalance", body: { version: 1, ...opts, }, }, (res) => res.parsedBody.result); } /** * Executes the given cluster move shard operations. * * @example * ```js * const db = new Database(); * const result = await db.computerClusterRebalance({ * moveLeaders: true, * moveFollowers: true * }); * if (result.moves.length) { * await db.executeClusterRebalance(result.moves); * } * ``` */ executeClusterRebalance(moves) { return this.request({ method: "POST", path: "/_admin/cluster/rebalance/execute", body: { version: 1, moves, }, }); } /** * Computes a set of move shard operations to rebalance the cluster and * executes them. * * @example * ```js * const db = new Database(); * const result = await db.rebalanceCluster({ * moveLeaders: true, * moveFollowers: true * }); * // The cluster is now rebalanced. * ``` */ rebalanceCluster(opts) { return this.request({ method: "PUT", path: "/_admin/cluster/rebalance", body: { version: 1, ...opts, }, }); } //#endregion //#region databases /** * Creates a new `Database` instance for the given `databaseName` that * shares this database's connection pool. * * See also {@link Database:constructor}. * * @param databaseName - Name of the database. * * @example * ```js * const systemDb = new Database(); * const myDb = system.database("my_database"); * ``` */ database(databaseName) { return new Database(this, databaseName); } /** * Fetches the database description for the active database from the server. * * @example * ```js * const db = new Database(); * const info = await db.get(); * // the database exists * ``` */ get() { return this.request({ path: "/_api/database/current" }, (res) => res.parsedBody.result); } /** * Checks whether the database exists. * * @example * ```js * const db = new Database(); * const result = await db.exists(); * // result indicates whether the database exists * ``` */ async exists() { try { await this.get(); return true; } catch (err) { if ((0, error_js_1.isArangoError)(err) && err.errorNum === codes_js_1.DATABASE_NOT_FOUND) { return false; } throw err; } } createDatabase(databaseName, usersOrOptions = {}) { const { users, ...options } = Array.isArray(usersOrOptions) ? { users: usersOrOptions } : usersOrOptions; return this.request({ method: "POST", path: "/_api/database", body: { name: databaseName, users, options }, }, () => this.database(databaseName)); } /** * Fetches all databases from the server and returns an array of their names. * * See also {@link Database#databases} and * {@link Database#listUserDatabases}. * * @example * ```js * const db = new Database(); * const names = await db.listDatabases(); * // databases is an array of database names * ``` */ listDatabases() { return this.request({ path: "/_api/database" }, (res) => res.parsedBody.result); } /** * Fetches all databases accessible to the active user from the server and * returns an array of their names. * * See also {@link Database#userDatabases} and * {@link Database#listDatabases}. * * @example * ```js * const db = new Database(); * const names = await db.listUserDatabases(); * // databases is an array of database names * ``` */ listUserDatabases() { return this.request({ path: "/_api/database/user" }, (res) => res.parsedBody.result); } /** * Fetches all databases from the server and returns an array of `Database` * instances for those databases. * * See also {@link Database#listDatabases} and * {@link Database#userDatabases}. * * @example * ```js * const db = new Database(); * const names = await db.databases(); * // databases is an array of databases * ``` */ databases() { return this.request({ path: "/_api/database" }, (res) => res.parsedBody.result.map((databaseName) => this.database(databaseName))); } /** * Fetches all databases accessible to the active user from the server and * returns an array of `Database` instances for those databases. * * See also {@link Database#listUserDatabases} and * {@link Database#databases}. * * @example * ```js * const db = new Database(); * const names = await db.userDatabases(); * // databases is an array of databases * ``` */ userDatabases() { return this.request({ path: "/_api/database/user" }, (res) => res.parsedBody.result.map((databaseName) => this.database(databaseName))); } /** * Deletes the database with the given `databaseName` from the server. * * @param databaseName - Name of the database to delete. * * @example * ```js * const db = new Database(); * await db.dropDatabase("mydb"); * // database "mydb" no longer exists * ``` */ dropDatabase(databaseName) { return this.request({ method: "DELETE", path: `/_api/database/${encodeURIComponent(databaseName)}`, }, (res) => res.parsedBody.result); } //#endregion //#region collections /** * Returns a `Collection` instance for the given collection name. * * In TypeScript the collection implements both the * {@link collection.DocumentCollection} and {@link collection.EdgeCollection} * interfaces and can be cast to either type to enforce a stricter API. * * @param T - Type to use for document data. Defaults to `any`. * @param collectionName - Name of the edge collection. * * @example * ```js * const db = new Database(); * const collection = db.collection("potatoes"); * ``` * * @example * ```ts * interface Person { * name: string; * } * const db = new Database(); * const persons = db.collection<Person>("persons"); * ``` * * @example * ```ts * interface Person { * name: string; * } * interface Friend { * startDate: number; * endDate?: number; * } * const db = new Database(); * const documents = db.collection("persons") as DocumentCollection<Person>; * const edges = db.collection("friends") as EdgeCollection<Friend>; * ``` */ collection(collectionName) { collectionName = collectionName; if (!this._collections.has(collectionName)) { this._collections.set(collectionName, new collection_js_1.Collection(this, collectionName)); } return this._collections.get(collectionName); } async createCollection(collectionName, options) { const collection = this.collection(collectionName); await collection.create(options); return collection; } /** * Creates a new edge collection with the given `collectionName` and * `options`, then returns an {@link collection.EdgeCollection} instance for the new * edge collection. * * This is a convenience method for calling {@link Database#createCollection} * with `options.type` set to `EDGE_COLLECTION`. * * @param T - Type to use for edge document data. Defaults to `any`. * @param collectionName - Name of the new collection. * @param options - Options for creating the collection. * * @example * ```js * const db = new Database(); * const edges = db.createEdgeCollection("friends"); * ``` * * @example * ```ts * interface Friend { * startDate: number; * endDate?: number; * } * const db = new Database(); * const edges = db.createEdgeCollection<Friend>("friends"); * ``` */ async createEdgeCollection(collectionName, options) { return this.createCollection(collectionName, { ...options, type: collection_js_1.CollectionType.EDGE_COLLECTION, }); } /** * Renames the collection `collectionName` to `newName`. * * Additionally removes any stored `Collection` instance for * `collectionName` from the `Database` instance's internal cache. * * **Note**: Renaming collections may not be supported when ArangoDB is * running in a cluster configuration. * * @param collectionName - Current name of the collection. * @param newName - The new name of the collection. */ async renameCollection(collectionName, newName) { const result = await this.request({ method: "PUT", path: `/_api/collection/${encodeURIComponent(collectionName)}/rename`, body: { name: newName }, }); this._collections.delete(collectionName); return result; } /** * Fetches all collections from the database and returns an array of * collection descriptions. * * See also {@link Database#collections}. * * @param excludeSystem - Whether system collections should be excluded. * * @example * ```js * const db = new Database(); * const collections = await db.listCollections(); * // collections is an array of collection descriptions * // not including system collections * ``` * * @example * ```js * const db = new Database(); * const collections = await db.listCollections(false); * // collections is an array of collection descriptions * // including system collections * ``` */ listCollections(excludeSystem = true) { return this.request({ path: "/_api/collection", search: { excludeSystem }, }, (res) => res.parsedBody.result); } /** * Fetches all collections from the database and returns an array of * `Collection` instances. * * In TypeScript these instances implement both the * {@link collection.DocumentCollection} and {@link collection.EdgeCollection} * interfaces and can be cast to either type to enforce a stricter API. * * See also {@link Database#listCollections}. * * @param excludeSystem - Whether system collections should be excluded. * * @example * ```js * const db = new Database(); * const collections = await db.collections(); * // collections is an array of DocumentCollection and EdgeCollection * // instances not including system collections * ``` * * @example * ```js * const db = new Database(); * const collections = await db.collections(false); * // collections is an array of DocumentCollection and EdgeCollection * // instances including system collections * ``` */ async collections(excludeSystem = true) { const collections = await this.listCollections(excludeSystem); return collections.map((data) => this.collection(data.name)); } //#endregion //#region graphs /** * Returns a {@link graph.Graph} instance representing the graph with the given * `graphName`. * * @param graphName - Name of the graph. * * @example * ```js * const db = new Database(); * const graph = db.graph("some-graph"); * ``` */ graph(graphName) { if (!this._graphs.has(graphName)) { this._graphs.set(graphName, new graph_js_1.Graph(this, graphName)); } return this._graphs.get(graphName); } /** * Creates a graph with the given `graphName` and `edgeDefinitions`, then * returns a {@link graph.Graph} instance for the new graph. * * @param graphName - Name of the graph to be created. * @param edgeDefinitions - An array of edge definitions. * @param options - An object defining the properties of the graph. */ async createGraph(graphName, edgeDefinitions, options) { const graph = this.graph(graphName); await graph.create(edgeDefinitions, options); return graph; } /** * Fetches all graphs from the database and returns an array of graph * descriptions. * * See also {@link Database#graphs}. * * @example * ```js * const db = new Database(); * const graphs = await db.listGraphs(); * // graphs is an array of graph descriptions * ``` */ listGraphs() { return this.request({ path: "/_api/gharial" }, (res) => res.parsedBody.graphs); } /** * Fetches all graphs from the database and returns an array of {@link graph.Graph} * instances for those graphs. * * See also {@link Database#listGraphs}. * * @example * ```js * const db = new Database(); * const graphs = await db.graphs(); * // graphs is an array of Graph instances * ``` */ async graphs() { const graphs = await this.listGraphs(); return graphs.map((data) => this.graph(data._key)); } //#endregion //#region views /** * Returns a {@link view.View} instance for the given `viewName`. * * @param viewName - Name of the ArangoSearch or SearchAlias View. * * @example * ```js * const db = new Database(); * const view = db.view("potatoes"); * ``` */ view(viewName) { if (!this._views.has(viewName)) { this._views.set(viewName, new view_js_1.View(this, viewName)); } return this._views.get(viewName); } /** * Creates a new View with the given `viewName` and `options`, then returns a * {@link view.View} instance for the new View. * * @param viewName - Name of the View. * @param options - An object defining the properties of the View. * * @example * ```js * const db = new Database(); * const view = await db.createView("potatoes", { type: "arangosearch" }); * // the ArangoSearch View "potatoes" now exists * ``` */ async createView(viewName, options) { const view = this.view(viewName); await view.create(options); return view; } /** * Renames the view `viewName` to `newName`. * * Additionally removes any stored {@link view.View} instance for `viewName` from * the `Database` instance's internal cache. * * **Note**: Renaming views may not be supported when ArangoDB is running in * a cluster configuration. * * @param viewName - Current name of the view. * @param newName - The new name of the view. */ async renameView(viewName, newName) { const result = await this.request({ method: "PUT", path: `/_api/view/${encodeURIComponent(viewName)}/rename`, body: { name: newName }, }); this._views.delete(viewName); return result; } /** * Fetches all Views from the database and returns an array of View * descriptions. * * See also {@link Database#views}. * * @example * ```js * const db = new Database(); * * const views = await db.listViews(); * // views is an array of View descriptions * ``` */ listViews() { return this.request({ path: "/_api/view" }, (res) => res.parsedBody.result); } /** * Fetches all Views from the database and returns an array of * {@link view.View} instances * for the Views. * * See also {@link Database#listViews}. * * @example * ```js * const db = new Database(); * const views = await db.views(); * // views is an array of ArangoSearch View instances * ``` */ async views() { const views = await this.listViews(); return views.map((data) => this.view(data.name)); } //#endregion //#region analyzers /** * Returns an {@link analyzer.Analyzer} instance representing the Analyzer with the * given `analyzerName`. * * @example * ```js * const db = new Database(); * const analyzer = db.analyzer("some-analyzer"); * const info = await analyzer.get(); * ``` */ analyzer(analyzerName) { if (!this._analyzers.has(analyzerName)) { this._analyzers.set(analyzerName, new analyzer_js_1.Analyzer(this, analyzerName)); } return this._analyzers.get(analyzerName); } /** * Creates a new Analyzer with the given `analyzerName` and `options`, then * returns an {@link analyzer.Analyzer} instance for the new Analyzer. * * @param analyzerName - Name of the Analyzer. * @param options - An object defining the properties of the Analyzer. * * @example * ```js * const db = new Database(); * const analyzer = await db.createAnalyzer("potatoes", { type: "identity" }); * // the identity Analyzer "potatoes" now exists * ``` */ async createAnalyzer(analyzerName, options) { const analyzer = this.analyzer(analyzerName); await analyzer.create(options); return analyzer; } /** * Fetches all Analyzers visible in the database and returns an array of * Analyzer descriptions. * * See also {@link Database#analyzers}. * * @example * ```js * const db = new Database(); * const analyzers = await db.listAnalyzers(); * // analyzers is an array of Analyzer descriptions * ``` */ listAnalyzers() { return this.request({ path: "/_api/analyzer" }, (res) => res.parsedBody.result); } /** * Fetches all Analyzers visible in the database and returns an array of * {@link analyzer.Analyzer} instances for those Analyzers. * * See also {@link Database#listAnalyzers}. * * @example * ```js * const db = new Database(); * const analyzers = await db.analyzers(); * // analyzers is an array of Analyzer instances * ``` */ async analyzers() { const analyzers = await this.listAnalyzers(); return analyzers.map((data) => this.analyzer(data.name)); } //#endregion /** * Fetches all ArangoDB users visible to the authenticated user and returns * an array of user objects. * * @example * ```js * const db = new Database(); * const users = await db.listUsers(); * // users is an array of user objects * ``` */ listUsers() { return this.request({ path: "/_api/user", }, (res) => res.parsedBody.result); } /** * Fetches the user data of a single ArangoDB user. * * @param username - Name of the ArangoDB user to fetch. * * @example * ```js * const db = new Database(); * const user = await db.getUser("steve"); * // user is the user object for the user named "steve" * ``` */ getUser(username) { return this.request({ path: `/_api/user/${encodeURIComponent(username)}`, }); } createUser(username, options) { if (typeof options === "string") { options = { passwd: options }; } return this.request({ method: "POST", path: "/_api/user", body: { user: username, ...options }, }, (res) => res.parsedBody); } updateUser(username, options) { if (typeof options === "string") { options = { passwd: options }; } return this.request({ method: "PATCH", path: `/_api/user/${encodeURIComponent(username)}`, body: options, }, (res) => res.parsedBody); } /** * Replaces the ArangoDB user's option with the new options. * * @param username - Name of the ArangoDB user to modify. * @param options - New options to replace the user's existing options. * * @example * ```js * const db = new Database(); * const user = await db.replaceUser("steve", { passwd: "", active: false }); * // The user "steve" has been set to inactive with an empty password * ``` */ replaceUser(username, options) { if (typeof options === "string") { options = { passwd: options }; } return this.request({ method: "PUT", path: `/_api/user/${encodeURIComponent(username)}`, body: options, }, (res) => res.parsedBody); } /** * Removes the ArangoDB user with the given username from the server. * * @param username - Name of the ArangoDB user to remove. * * @example * ```js * const db = new Database(); * await db.removeUser("steve"); * // The user "steve" has been removed * ``` */ removeUser(username) { return this.request({ method: "DELETE", path: `/_api/user/${encodeURIComponent(username)}`, }, (res) => res.parsedBody); } /** * Fetches the given ArangoDB user's access level for the database, or the * given collection in the given database. * * @param username - Name of the ArangoDB user to fetch the access level for. * @param database - Database to fetch the access level for. * @param collection - Collection to fetch the access level for. * * @example * ```js * const db = new Database(); * const accessLevel = await db.getUserAccessLevel("steve"); * // The access level of the user "steve" has been fetched for the current * // database. * ``` * * @example * ```js * const db = new Database(); * const accessLevel = await db.getUserAccessLevel("steve", { * database: "staging" * }); * // The access level of the user "steve" has been fetched for the "staging" * // database. * ``` * * @example * ```js * const db = new Database(); * const accessLevel = await db.getUserAccessLevel("steve", { * collection: "pokemons" * }); * // The access level of the user "steve" has been fetched for the * // "pokemons" collection in the current database. * ``` * * @example * ```js * const db = new Database(); * const accessLevel = await db.getUserAccessLevel("steve", { * database: "staging", * collection: "pokemons" * }); * // The access level of the user "steve" has been fetched for the * // "pokemons" collection in the "staging" database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * const accessLevel = await db.getUserAccessLevel("steve", { * database: staging * }); * // The access level of the user "steve" has been fetched for the "staging" * // database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * const accessLevel = await db.getUserAccessLevel("steve", { * collection: staging.collection("pokemons") * }); * // The access level of the user "steve" has been fetched for the * // "pokemons" collection in database "staging". * ``` */ getUserAccessLevel(username, { database, collection }) { const databaseName = isArangoDatabase(database) ? database.name : database ?? ((0, collection_js_1.isArangoCollection)(collection) ? collection._db.name : this._name); const suffix = collection ? `/${encodeURIComponent((0, collection_js_1.isArangoCollection)(collection) ? collection.name : collection)}` : ""; return this.request({ path: `/_api/user/${encodeURIComponent(username)}/database/${encodeURIComponent(databaseName)}${suffix}`, }, (res) => res.parsedBody.result); } /** * Sets the given ArangoDB user's access level for the database, or the * given collection in the given database. * * @param username - Name of the ArangoDB user to set the access level for. * @param database - Database to set the access level for. * @param collection - Collection to set the access level for. * @param grant - Access level to set for the given user. * * @example * ```js * const db = new Database(); * await db.setUserAccessLevel("steve", { grant: "rw" }); * // The user "steve" now has read-write access to the current database. * ``` * * @example * ```js * const db = new Database(); * await db.setUserAccessLevel("steve", { * database: "staging", * grant: "rw" * }); * // The user "steve" now has read-write access to the "staging" database. * ``` * * @example * ```js * const db = new Database(); * await db.setUserAccessLevel("steve", { * collection: "pokemons", * grant: "rw" * }); * // The user "steve" now has read-write access to the "pokemons" collection * // in the current database. * ``` * * @example * ```js * const db = new Database(); * await db.setUserAccessLevel("steve", { * database: "staging", * collection: "pokemons", * grant: "rw" * }); * // The user "steve" now has read-write access to the "pokemons" collection * // in the "staging" database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * await db.setUserAccessLevel("steve", { * database: staging, * grant: "rw" * }); * // The user "steve" now has read-write access to the "staging" database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * await db.setUserAccessLevel("steve", { * collection: staging.collection("pokemons"), * grant: "rw" * }); * // The user "steve" now has read-write access to the "pokemons" collection * // in database "staging". * ``` */ setUserAccessLevel(username, { database, collection, grant, }) { const databaseName = isArangoDatabase(database) ? database.name : database ?? ((0, collection_js_1.isArangoCollection)(collection) ? collection._db.name : this._name); const suffix = collection ? `/${encodeURIComponent((0, collection_js_1.isArangoCollection)(collection) ? collection.name : collection)}` : ""; return this.request({ method: "PUT", path: `/_api/user/${encodeURIComponent(username)}/database/${encodeURIComponent(databaseName)}${suffix}`, body: { grant }, }, (res) => res.parsedBody); } /** * Clears the given ArangoDB user's access level for the database, or the * given collection in the given database. * * @param username - Name of the ArangoDB user to clear the access level for. * @param database - Database to clear the access level for. * @param collection - Collection to clear the access level for. * * @example * ```js * const db = new Database(); * await db.clearUserAccessLevel("steve"); * // The access level of the user "steve" has been cleared for the current * // database. * ``` * * @example * ```js * const db = new Database(); * await db.clearUserAccessLevel("steve", { database: "staging" }); * // The access level of the user "steve" has been cleared for the "staging" * // database. * ``` * * @example * ```js * const db = new Database(); * await db.clearUserAccessLevel("steve", { collection: "pokemons" }); * // The access level of the user "steve" has been cleared for the * // "pokemons" collection in the current database. * ``` * * @example * ```js * const db = new Database(); * await db.clearUserAccessLevel("steve", { * database: "staging", * collection: "pokemons" * }); * // The access level of the user "steve" has been cleared for the * // "pokemons" collection in the "staging" database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * await db.clearUserAccessLevel("steve", { database: staging }); * // The access level of the user "steve" has been cleared for the "staging" * // database. * ``` * * @example * ```js * const db = new Database(); * const staging = db.database("staging"); * await db.clearUserAccessLevel("steve", { * collection: staging.collection("pokemons") * }); * // The access level of the user "steve" has been cleared for the * // "pokemons" collection in database "staging". * ``` */ clearUserAccessLevel(username, { database, collection }) { const databaseName = isArangoDatabase(database) ? database.name : database ?? ((0, collection_js_1.isArangoCollection)(collection) ? collection._db.name : this._name); const suffix = collection ? `/${encodeURIComponent((0, collection_js_1.isArangoCollection)(collection) ? collection.name : collection)}` : ""; return this.request({ method: "DELETE", path: `/_api/user/${encodeURIComponent(username)}/database/${encodeURIComponent(databaseName)}${suffix}`, }, (res) => res.parsedBody); } getUserDatabases(username, full) { return this.request({ path: `/_api/user/${encodeURIComponent(username)}/database`, search: { full }, }, (res) => res.parsedBody.result); } executeTransaction(collections, action, options = {}) { const { allowDirtyRead = undefined, ...opts } = options; return this.request({ method: "POST", path: "/_api/transaction", allowDirtyRead, body: { collections: coerceTransactionCollections(collections), action, ...opts, }, }, (res) => res.parsedBody.result); } /** * Returns a {@link transaction.Transaction} instance for an existing streaming * transaction with the given `id`. * * See also {@link Database#beginTransaction}. * * @param id - The `id` of an existing stream transaction. * * @example * ```js * const trx1 = await db.beginTransaction(collections); * const id = trx1.id; * // later * const trx2 = db.transaction(id); * await trx2.commit(); * ``` */ transaction(transactionId) { return new transaction_js_1.Transaction(this, transactionId); } beginTransaction(collections, options = {}) { const { allowDirtyRead = undefined, ...opts } = options; return this.request({ method: "POST", path: "/_api/transaction/begin", allowDirtyRead, body: { collections: coerceTransactionCollections(collections), ...opts, }, }, (res) => new transaction_js_1.Transaction(this, res.parsedBody.result.id)); } async withTransaction(collections, callback, options = {}) { const trx = await this.beginTransaction(collections, options); try { const result = await callback((fn) => trx.step(fn)); await trx.commit(); return result; } catch (e) { try { await trx.abort(); } catch { } throw e; } } /** * Fetches all active transactions from the database and returns an array of * transaction descriptions. * * See also {@link Database#transactions}. * * @example * ```js * const db = new Database(); * const transactions = await db.listTransactions(); * // transactions is an array of transaction descriptions * ``` */ listTransactions() { return this._connection.request({ path: "/_api/transaction" }, (res) => res.parsedBody.transactions); } /** * Fetches all active transactions from the database and returns an array of * {@link transaction.Transaction} instances for those transactions. * * See also {@link Database#listTransactions}. * * @example * ```js * const db = new Database(); * const transactions = await db.transactions(); * // transactions is an array of transactions * ``` */ async transactions() { const transactions = await this.listTransactions();