arangojs
Version:
The official ArangoDB JavaScript driver.
2,834 lines • 92.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Database = exports.isArangoDatabase = void 0;
const analyzers = __importStar(require("./analyzers.js"));
const aql = __importStar(require("./aql.js"));
const collections = __importStar(require("./collections.js"));
const connection = __importStar(require("./connection.js"));
const cursors = __importStar(require("./cursors.js"));
const errors = __importStar(require("./errors.js"));
const graphs = __importStar(require("./graphs.js"));
const jobs = __importStar(require("./jobs.js"));
const codes_js_1 = require("./lib/codes.js");
const util = __importStar(require("./lib/util.js"));
const routes = __importStar(require("./routes.js"));
const transactions = __importStar(require("./transactions.js"));
const views = __importStar(require("./views.js"));
//#region Database class
/**
* 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;
/**
* 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.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;
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Returns a new {@link routes.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 routes.Route(this, path, headers);
}
async request({ pathname, ...opts }, transform = (res) => res.parsedBody) {
pathname = util.joinPath("_db", encodeURIComponent(this._name), pathname);
if (this._trapRequest) {
const trap = this._trapRequest;
this._trapRequest = undefined;
return new Promise(async (resolveRequest, rejectRequest) => {
opts.headers = new Headers(opts.headers);
opts.headers.set("x-arango-async", "store");
let jobRes;
try {
jobRes = await this._connection.request({ pathname, ...opts });
}
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({ pathname, ...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 configuration.ConfigOptions}.
*
* 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({ pathname: "/_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();
}
async waitForPropagation({ pathname, ...request }, timeout) {
await this._connection.waitForPropagation({
...request,
pathname: util.joinPath("_db", encodeURIComponent(this._name), pathname),
}, 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;
}
/**
* Updates the underlying connection's `authorization` header to use Basic
* authentication with the given access token, then returns itself.
*
* Access tokens contain embedded username information, so the username field
* in Basic Auth can be empty. The token acts as a password replacement.
*
* @param token - The access token to authenticate with.
*
* @example
* ```js
* const db = new Database();
* const tokenResult = await db.createAccessToken("appUser", {
* name: "CI token"
* });
* db.useAccessToken(tokenResult.token);
* // The database instance now uses the access token for authentication.
* ```
*/
useAccessToken(token) {
if (!token || typeof token !== "string") {
throw new Error("Token must be a non-empty string");
}
// Access tokens can be used with empty username in Basic Auth
// The token contains embedded username information
return this.useBasicAuth("", token);
}
/**
* 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",
pathname: "/_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",
pathname: "/_open/auth/renew",
}, (res) => {
if (!res.parsedBody.jwt)
return null;
this.useBearerAuth(res.parsedBody.jwt);
return res.parsedBody.jwt;
});
}
//#endregion
//#region administration
/**
* 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",
pathname: "/_api/version",
search: { details },
});
}
/**
* Fetches storage engine information from the ArangoDB server.
*
* @example
* ```js
* const db = new Database();
* const engine = await db.engine();
* // the engine object contains the storage engine information, e.g.
* // name: name of the storage engine
* ```
*/
engine() {
return this.request({
method: "GET",
pathname: "/_api/engine",
});
}
/**
* Fetches detailed storage engine performance and resource usage information
* from the ArangoDB server.
*
* @example
* ```js
* const db = new Database();
* const stats = await db.engineStats();
* // the stats object contains the storage engine stats
* ```
*/
engineStats() {
return this.request({
method: "GET",
pathname: "/_api/engine/stats",
});
}
/**
* Retrives the server's current system time in milliseconds with microsecond
* precision.
*/
time() {
return this.request({
method: "GET",
pathname: "/_admin/time",
}, (res) => res.parsedBody.time * 1000);
}
/**
* Fetches information about the server status.
*
* @example
* ```js
* const status = await db.status();
* // the status object contains the ArangoDB status information, e.g.
* // version: ArangoDB version number
* // host: host identifier of the server
* // serverInfo: detailed information about the server
* ```
*/
status() {
return this.request({
method: "GET",
pathname: "/_admin/status",
});
}
/**
* Fetches availability information about the server.
*
* @param graceful - If set to `true`, the method will always return `false`
* instead of throwing an error; otherwise `false` will only be returned
* when the server responds with a 503 status code or an ArangoDB error with
* a code of 503, such as during shutdown.
*
* @example
* ```js
* const availability = await db.availability();
* // availability is either "default", "readonly", or false
* ```
*/
async availability(graceful = false) {
try {
return this.request({
method: "GET",
pathname: "/_admin/server/availability",
}, (res) => res.parsedBody.mode);
}
catch (e) {
if (graceful)
return false;
if ((errors.isArangoError(e) || e instanceof errors.HttpError) &&
e.code === 503) {
return false;
}
throw e;
}
}
/**
* Fetches deployment information about the server for support purposes.
*
* Note that this API may reveal sensitive data about the deployment.
*/
supportInfo() {
return this.request({
method: "GET",
pathname: "/_admin/support-info",
});
}
/**
* Fetches the license information and status of an Enterprise Edition server.
*/
getLicense() {
return this.request({
method: "GET",
pathname: "/_admin/license",
});
}
/**
* Set a new license for an Enterprise Edition server.
*
* @param license - The license as a base 64 encoded string.
* @param force - If set to `true`, the license will be changed even if it
* expires sooner than the current license.
*/
setLicense(license, force = false) {
return this.request({
method: "PUT",
pathname: "/_admin/license",
body: license,
search: { force },
}, () => undefined);
}
/**
* Compacts all databases on the server.
*
* @param options - Options for compacting the databases.
*/
compact(options = {}) {
return this.request({
method: "PUT",
pathname: "/_admin/compact",
body: options,
}, () => undefined);
}
/**
* Attempts to initiate a clean shutdown of the server.
*/
shutdown() {
return this.request({
method: "DELETE",
pathname: "/_admin/shutdown",
}, () => undefined);
}
//#endregion
//#region rebalancing
/**
* Computes the current cluster imbalance.
*
* @example
* ```js
* const db = new Database();
* const imbalance = await db.getClusterImbalance();
* ```
*/
getClusterImbalance() {
return this.request({ pathname: "/_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(options) {
return this.request({
method: "POST",
pathname: "/_admin/cluster/rebalance",
body: {
version: 1,
...options,
},
}, (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",
pathname: "/_admin/cluster/rebalance/execute",
body: {
version: 1,
moves,
},
});
}
/**
* Computes a set of move shard operations to rebalance the cluster and
* executes them.
*
* @param options - Options for rebalancing the cluster.
*
* @example
* ```js
* const db = new Database();
* const result = await db.rebalanceCluster({
* moveLeaders: true,
* moveFollowers: true
* });
* // The cluster is now rebalanced.
* ```
*/
rebalanceCluster(options) {
return this.request({
method: "PUT",
pathname: "/_admin/cluster/rebalance",
body: {
version: 1,
...options,
},
});
}
//#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 = systemDb.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({ pathname: "/_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 (errors.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",
pathname: "/_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({ pathname: "/_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({ pathname: "/_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({ pathname: "/_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({ pathname: "/_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",
pathname: `/_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 collections.DocumentCollection} and {@link collections.EdgeCollection}
* interfaces and can be cast to either type to enforce a stricter API.
*
* @param EntryResultType - Type to represent document contents returned by
* the server (including computed properties).
* @param EntryInputType - Type to represent document contents passed when
* inserting or replacing documents (without computed properties).
* @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 collections.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 collections.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 EntryResultType - Type to represent edge document contents returned
* by the server (including computed properties).
* @param EntryInputType - Type to represent edge document contents passed
* when inserting or replacing documents (without computed properties).
* @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: collections.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",
pathname: `/_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({
pathname: "/_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 collections.DocumentCollection} and {@link collections.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 graphs.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 graphs.Graph(this, graphName));
}
return this._graphs.get(graphName);
}
/**
* Creates a graph with the given `graphName` and `edgeDefinitions`, then
* returns a {@link graphs.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({ pathname: "/_api/gharial" }, (res) => res.parsedBody.graphs);
}
/**
* Fetches all graphs from the database and returns an array of {@link graphs.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 views.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 views.View(this, viewName));
}
return this._views.get(viewName);
}
/**
* Creates a new View with the given `viewName` and `options`, then returns a
* {@link views.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 views.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",
pathname: `/_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({ pathname: "/_api/view" }, (res) => res.parsedBody.result);
}
/**
* Fetches all Views from the database and returns an array of
* {@link views.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 analyzers.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 analyzers.Analyzer(this, analyzerName));
}
return this._analyzers.get(analyzerName);
}
/**
* Creates a new Analyzer with the given `analyzerName` and `options`, then
* returns an {@link analyzers.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({ pathname: "/_api/analyzer" }, (res) => res.parsedBody.result);
}
/**
* Fetches all Analyzers visible in the database and returns an array of
* {@link analyzers.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
//#region users
/**
* 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({
pathname: "/_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({
pathname: `/_api/user/${encodeURIComponent(username)}`,
});
}
createUser(username, options) {
if (typeof options === "string") {
options = { passwd: options };
}
return this.request({
method: "POST",
pathname: "/_api/user",
body: { user: username, ...options },
}, (res) => res.parsedBody);
}
updateUser(username, options) {
if (typeof options === "string") {
options = { passwd: options };
}
return this.request({
method: "PATCH",
pathname: `/_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",
pathname: `/_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",
pathname: `/_api/user/${encodeURIComponent(username)}`,
}, () => undefined);
}
/**
* 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 options - Collection and/or database 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, options) {
const { database, collection } = options;
const databaseName = isArangoDatabase(database)
? database.name
: (database ??
(collection instanceof collections.Collection
? collection.database.name
: this._name));
const suffix = collection
? `/${encodeURIComponent(collections.isArangoCollection(collection)
? collection.name
: collection)}`
: "";
return this.request({
pathname: `/_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 options - Database and/or 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, options, grant) {
const { database, collection } = options;
const databaseName = isArangoDatabase(database)
? database.name
: (database ??
(collection instanceof collections.Collection
? collection.database.name
: this._name));
const suffix = collection
? `/${encodeURIComponent(collections.isArangoCollection(collection)
? collection.name
: collection)}`
: "";
return this.request({
method: "PUT",
pathname: `/_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 options - Database and/or 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, options) {
const { database, collection } = options;
const databaseName = isArangoDatabase(database)
? database.name
: (database ??
(collection instanceof collections.Collection
? collection.database.name
: this._name));
const suffix = collection
? `/${encodeURIComponent(collections.isArangoCollection(collection)
? collection.name
: collection)}`
: "";
return this.request({
method: "DELETE",
pathname: `/_api/user/${encodeURIComponent(username)}/database/${encodeURIComponent(databaseName)}${suffix}`,
}, (res) => res.parsedBody);
}
getUserDatabases(username, full) {
return this.request({
pathname: `/_api/user/${encodeURIComponent(username)}/database`,
search: { full },
}, (res) => res.parsedBody.result);
}
/**
* Creates a new access token for the given ArangoDB user.
*
* Access tokens can be used as password replacements for authentication.
* The token value is only returned once in this response and cannot be
* retrieved again. Store it securely.
*
* @param username - Name of the ArangoDB user to create the token for.
* @param options - Options for creating the access token.
*
* @example
* ```js
* const db = new Database();
* // Convert Date to Unix timestamp (seconds)
* const expiryDate = new Date("2025-12-31");
* const expiryTimestamp = Math.floor(expiryDate.getTime() / 1000);
* const token = await db.createAccessToken("appUser", {
* name: "CI token",
* valid_until: expiryTimestamp
* });
* console.log(token.token); // Store this securely!
* ```
*
* @example
* ```js
* const db = new Database();
* const token = await db.createAccessToken("appUser", {
* name: "Production token",
* valid_until: Math.floor(Date.now() / 1000) + (30 * 24 * 60 * 60) // 30 days
* });
* ```
*/
createAccessToken(username, options) {
if (!username || typeof username !== "string") {
throw new Error("Username must be a non-empty string");
}
if (!options || typeof options !== "object") {
throw new Error("Options must be an object");
}
if (!options.name || typeof options.name !== "string") {
throw new Error("Token name must be a non-empty string");
}
const body = {
name: options.name,
};
if (options.valid_until !== undefined) {
if (typeof options.valid_until !== "number") {
throw new Error("valid_until must be a Unix timestamp (number in seconds)");
}
body.valid_until = options.valid_until;
}
return this.request({
method: "POST",
pathname: `/_api/token/${encodeURIComponent(username)}`,
body,
}, (res) => res.parsedBody);
}
/**
* Fetches all access tokens for the given ArangoDB user.
*
* Note: Token values are not returned. Only metadata (id, name, expiry, etc.)
* is available. Token values can only be retrieved when creating a new token.
*
* @param username - Name of the ArangoDB user to fetch tokens for.
*
* @example
* ```js
* const db = new Database();
* const tokens = await db.getAccessTokens("appUser");
* for (const token of tokens) {
* console.log(`${token.name}: expires ${new Date(token.valid_until * 1000)}`);
* }
* ```
*/
getAccessTokens(username) {
if (!username || typeof username !== "string") {
throw new Error("Username must be a non-empty string");
}
return this.request({
pathname: `/_api/token/${encodeURIComponent(username)}`,
}, (res) => res.parsedBody.tokens);
}
/**
* Deletes (revokes) an access token for the given ArangoDB user.
*
* Once deleted, the token cannot be used for authentication and cannot be recovered.
*
* @param username - Name of the ArangoDB user who owns the token.
* @param tokenId - Unique identifier of the token to delete.
*
* @example
* ```js
* const db = new Database();
* const tokens = await db.getAccessTokens("appUser");
* for (const token of tokens) {
* if (token.name === "Old CI token") {
* await db.deleteAccessToken("appUser", token.id);
* }
* }
* ```
*/
deleteAccessToken(username, tokenId) {
if (!username || typeof username !== "string") {
throw new Error("Username must be a non-empty string");
}
if (typeof tokenId !== "number" || !Number.isInteger(tokenId) || tokenId < 0) {
throw new Error("Token ID must be a non-negative integer");
}
return this.request({
method: "DELETE",
pathname: `/_api/token/${encodeURIComponent(username)}/${tokenId}`,
}, () => undefined);
}
executeTransaction(collections, action, options = {}) {
const { allowDirtyRead = undefined, ...opts } = options;
return this.request({
method: "POST",
pathname: "/_api/transaction",
allowDirtyRead,
body: {
collections: transactions.coerceTransactionCollections(collections),
action,
...opts,
},
}, (res) => res.parsedBody.result);
}
/**
* Returns a {@link transactions.Transaction} instance for an existing streaming
* transaction with the given `id`.
*
* See also {@link Database#beginTransaction}.
*
* @param transactionId - 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 transactions.Transaction(this, transactionId);
}
beginTransaction(collections, options = {}) {
const { allowDirtyRead = undefined, ...opts } = options;
return this.request({
method: "POST",
pathname: "/_api/transaction/begin",
allowDirtyRead,
body: {
collections: transactions.coerceTransactionCollections(collections),
...opts,
},
}, (res) => new transactions.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({ pathname: "/_api/transaction" }, (res) => res.parsedBody.transactions);
}
/**
* Fetches all active transactions from the database and returns an array of
* {@link transactions.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();
return transactions.map((data) => this.transaction(data.id));
}
query(query, bindVars, options = {}) {
if (aql.isAqlQuery(query)) {
options = bindVars ?? {};
bindVars = query.bindVars;
query = query.query;
}
else if (aql.isAqlLiteral(query)) {
query = query.toAQL();
}
const { allowDirtyRead, retryOnConflict, count, batchSize, cache, memoryLimit, ttl, timeout, ...opts } = options;
// Accept legacy maxPlans and new maxNumberOfPlans; always send maxNumberOfPlans
const { maxPlans, maxNumberOfPlans, ...remainingOpts } = opts;
const normalizedOptions = {
...remainingOpts,
...(maxNumberOfPlans !== undefined
? { maxNumberOfPlans }
: maxPlans !== undefined
? { maxNumberOfPlans: maxPlans }
: {}),
};
return this.request({
method: "POST",
pathname: "/_api/cursor",
body: {
query,
bindVars,
count,
batchSize,
cache,
memoryLimit,
ttl,
options: normalizedOptions,
},
allowDirtyRead,
retryOnConflict,
timeout,
}, (res) => new cursors.BatchCursor(this, res.parsedBody, res.arangojsHostUrl, allowDirtyRead).items);
}
explain(query, bindVars, options) {
if (aql.isAqlQuery(query)) {
options = bindVars;
bindVars = query.bindVars;
query = query.query;
}
else if (aql.isAqlLiteral(query)) {
query = query.toAQL();
}
return this.request({
method: "POST",
pathname: "/_api/explain",
body: { query, bindVars, options },
});
}
/**
* Parses the given query and returns the result.
*
* See the {@link aql.aql} template string handler for information about how
* to create a query string without manually defining bind parameters nor
* having to worry about escaping variables.
*
* @param query - An AQL query string or an object containing an AQL query
* string and bind parameters, e.g. the object returned from an {@link aql.aql}
* template string.
*
* @example
* ```js
* const db = new Database();
* const collection = db.collection("some-collection");
* const ast = await db.parse(aql`
* FOR doc IN ${collection}
* FILTER doc.flavor == "strawberry"
* RETURN doc._key
* `);
* ```
aql.*/
parse(query) {
if (aql.isAqlQuery(query)) {
query = query.query;
}
else if (aql.isAqlLiteral(query)) {
query = query.toAQL();
}
return this.request({
method: "POST",
pathname: "/_api/query",
body: { query },
});
}
/**
* Fetches the available optimizer rules.
*
* @example
* ```js
* const db = new Database();
* const rules = await db.queryRules();
* for (const rule of rules) {
* console.log(rule.name);
* }
* ```
*/
queryRules() {
return this.request({
pathname: "/_api/query/rules",
});
}
queryTracking(options) {
return this.request(options
? {
method: "PUT",
pathname: "/_api/query/properties",
body: options,
}
: {
method: "GET",
pathname: "/_api/query/properties",
});
}
/**
* Fetches a list of information for all currently running queries.
*
* See also {@link Database#listSlowQueries} and {@link Database#killQuery}.
*
* @example
* ```js
* const db = new Database();
* const queries = await db.listRunningQueries();
* ```
*/
listRunningQueries() {
return this.request({
method: "GET",
pathname: "/_api/query/current",
});
}
/**
* Fetches a list of information for all recent slow queries.
*
* See also {@link Database#listRunningQueries} and
* {@link Database#clearSlowQueries}.
*
* @example
* ```js
* const db = new Database();
* const queries = await db.listSlowQueries();
* // Only works if slow query tracking is enabled
* ```
*/
listSlowQueries() {
return this.request({
method: "GET",
pathname: "/_api/query/slow",
});
}
/**
* Clears the list of recent slow queries.
*
* See also {@link Database#listSlowQueries}.
*
* @example
* ```js
* const db = new Database();
* await db.clearSlowQueries();
* // Slow query list is now cleared
* ```
*/
clearSlowQueries() {
return this.request({
method: "DELETE",
pathname: "/_api/query/slow",
}, () => undefined);
}
/**
* Kills a running query with the given `queryId`.
*
* See also {@link Database#listRunningQueries}.
*
* @param queryId - The ID of a currently running query.
*
* @example
* ```js
* const db = new Database();
* const queries = await db.listRunningQueries();
* await Promise.all(queries.map(
* async (query) => {
* if (query.state === "executing") {
* await db.killQuery(query.id);
* }
* }
* ));
* ```
*/
killQuery(queryId) {
return this.request({
method: "DELETE",
pathname: `/_api/query/${encodeURIComponent(queryId)}`,
}, () => undefined);
}
/**
* Fetches a list of all entries in the AQL query results cache of the
* current database.
*
* @example
* ```js
* const db = new Database();
* const entries = await db.listQueryCacheEntries();
* console.log(entries);
* ```
*/
listQueryCacheEntries() {
return this.request({
pathname: "/_api/query-cache/entries",
});
}
/**
* Clears the AQL query results cache of the current database.
*
* @example
* ```js
* const db = new Database();
* await db.clearQueryCache();
* // Cache is now cleared
* ```
*/
clearQueryCache() {
return this.request({
method: "DELETE",
pathname: "/_api/query-cache",
}, () => undefined);
}
/**
* Fetches the global properties for the AQL query results cache.
*
* @example
* ```js
* const db = new Database();
* const properties = await db.getQueryCacheProperties();
* console.log(properties);
* ```
*/
getQueryCacheProperties() {
return this.request({
pathname: "/_api/query-cache/properties",
});
}
/**
* Updates the global properties for the AQL query results cache.
*
* @param properties - The new properties for the AQL query results cache.
*
* @example
* ```js
* const db = new Database();
* await db.setQueryCacheProperties({ maxResults: 9000 });
* ```
*/
setQueryCacheProperties(properties) {
return this.request({
method: "PUT",
pathname: "/_api/query-cache/properties",
body: properties,
});
}
//#endregion
//#region user functions
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Fetches a list of all AQL user functions registered with the database.
*
* @example
* ```js
* const db = new Database();
* const functions = await db.listUserFunctions();
* const names = functions.map(fn => fn.name);
* ```
*/
listUserFunctions() {
return this.request({ pathname: "/_api/aqlfunction" }, (res) => res.parsedBody.result);
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Creates an AQL user function with the given _name_ and _code_ if it does
* not already exist or replaces it if a function with the same name already
* existed.
*
* @param name - A valid AQL function name. The function name must consist
* of at least two alphanumeric identifiers separated with double colons.
* @param code - A string evaluating to a JavaScript function (not a
* JavaScript function object).
* @param isDeterministic - If set to `true`, the function is expected to
* always return the same result for equivalent inputs. This option currently
* has no effect but may allow for optimizations in the future.
*
* @example
* ```js
* const db = new Database();
* await db.createUserFunction(
* "ACME::ACCOUNTING::CALCULATE_VAT",
* "(price) => price * 0.19"
* );
* // Use the new function in an AQL query with template handler:
* const cursor = await db.query(aql`
* FOR product IN products
* RETURN MERGE(
* { vat: ACME::ACCOUNTING::CALCULATE_VAT(product.price) },
* product
* )
* `);
* // cursor is a cursor for the query result
* ```
*/
createUserFunction(name, code, isDeterministic = false) {
return this.request({
method: "POST",
pathname: "/_api/aqlfunction",
body: { name, code, isDeterministic },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Deletes the AQL user function with the given name from the database.
*
* @param name - The name of the user function to drop.
* @param group - If set to `true`, all functions with a name starting with
* `name` will be deleted, otherwise only the function with the exact name
* will be deleted.
*
* @example
* ```js
* const db = new Database();
* await db.dropUserFunction("ACME::ACCOUNTING::CALCULATE_VAT");
* // the function no longer exists
* ```
*/
dropUserFunction(name, group = false) {
return this.request({
method: "DELETE",
pathname: `/_api/aqlfunction/${encodeURIComponent(name)}`,
search: { group },
});
}
//#endregion
//#region services
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Fetches a list of all installed service.
*
* @param excludeSystem - Whether system services should be excluded.
*
* @example
* ```js
* const db = new Database();
* const services = await db.listServices();
* ```
*
* @example
* ```js
* const db = new Database();
* const services = await db.listServices(false); // all services
* ```
*/
listServices(excludeSystem = true) {
return this.request({
pathname: "/_api/foxx",
search: { excludeSystem },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Installs a new service.
*
* @param mount - The service's mount point, relative to the database.
* @param source - The service bundle to install.
* @param options - Options for installing the service.
*
* @example
* ```js
* const db = new Database();
* // Using a Buffer in Node.js as source
* const source = new Blob([await fs.readFileSync("./my-foxx-service.zip")]);
* const info = await db.installService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a Blob in Node.js as source
* const source = await fs.openAsBlob("./my-foxx-service.zip");
* const info = await db.installService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a File from a browser file input as source
* const element = document.getElementById("my-file-input");
* const source = element.files[0];
* const info = await db.installService("/hello", source);
* ```
*/
async installService(mount, source, options = {}) {
const { configuration, dependencies, ...search } = options;
const form = new FormData();
if (configuration) {
form.append("configuration", JSON.stringify(configuration));
}
if (dependencies) {
form.append("dependencies", JSON.stringify(dependencies));
}
form.append("source", typeof source === "string" ? JSON.stringify(source) : source);
return await this.request({
body: form,
method: "POST",
pathname: "/_api/foxx",
search: { ...search, mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Replaces an existing service with a new service by completely removing the
* old service and installing a new service at the same mount point.
*
* @param mount - The service's mount point, relative to the database.
* @param source - The service bundle to install.
* @param options - Options for replacing the service.
*
* @example
* ```js
* const db = new Database();
* // Using a Buffer in Node.js as source
* const source = new Blob([await fs.readFileSync("./my-foxx-service.zip")]);
* const info = await db.replaceService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a Blob in Node.js as source
* const source = await fs.openAsBlob("./my-foxx-service.zip");
* const info = await db.replaceService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a File from a browser file input as source
* const element = document.getElementById("my-file-input");
* const source = element.files[0];
* const info = await db.replaceService("/hello", source);
* ```
*/
async replaceService(mount, source, options = {}) {
const { configuration, dependencies, ...search } = options;
const form = new FormData();
if (configuration) {
form.append("configuration", JSON.stringify(configuration));
}
if (dependencies) {
form.append("dependencies", JSON.stringify(dependencies));
}
form.append("source", typeof source === "string" ? JSON.stringify(source) : source);
return await this.request({
body: form,
method: "PUT",
pathname: "/_api/foxx/service",
search: { ...search, mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Replaces an existing service with a new service while retaining the old
* service's configuration and dependencies.
*
* @param mount - The service's mount point, relative to the database.
* @param source - The service bundle to install.
* @param options - Options for upgrading the service.
*
* @example
* ```js
* const db = new Database();
* // Using a Buffer in Node.js as source
* const source = new Blob([await fs.readFileSync("./my-foxx-service.zip")]);
* const info = await db.upgradeService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a Blob in Node.js as source
* const source = await fs.openAsBlob("./my-foxx-service.zip");
* const info = await db.upgradeService("/hello", source);
* ```
*
* @example
* ```js
* const db = new Database();
* // Using a File from a browser file input as source
* const element = document.getElementById("my-file-input");
* const source = element.files[0];
* const info = await db.upgradeService("/hello", source);
* ```
*/
async upgradeService(mount, source, options = {}) {
const { configuration, dependencies, ...search } = options;
const form = new FormData();
if (configuration) {
form.append("configuration", JSON.stringify(configuration));
}
if (dependencies) {
form.append("dependencies", JSON.stringify(dependencies));
}
form.append("source", typeof source === "string" ? JSON.stringify(source) : source);
return await this.request({
body: form,
method: "PATCH",
pathname: "/_api/foxx/service",
search: { ...search, mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Completely removes a service from the database.
*
* @param mount - The service's mount point, relative to the database.
* @param options - Options for uninstalling the service.
*
* @example
* ```js
* const db = new Database();
* await db.uninstallService("/my-foxx");
* ```
*/
uninstallService(mount, options) {
return this.request({
method: "DELETE",
pathname: "/_api/foxx/service",
search: { ...options, mount },
}, () => undefined);
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Retrieves information about a mounted service.
*
* @param mount - The service's mount point, relative to the database.
*
* @example
* ```js
* const db = new Database();
* const info = await db.getService("/my-service");
* // info contains detailed information about the service
* ```
*/
getService(mount) {
return this.request({
pathname: "/_api/foxx/service",
search: { mount },
});
}
getServiceConfiguration(mount, minimal = false) {
return this.request({
pathname: "/_api/foxx/configuration",
search: { mount, minimal },
});
}
replaceServiceConfiguration(mount, cfg, minimal = false) {
return this.request({
method: "PUT",
pathname: "/_api/foxx/configuration",
body: cfg,
search: { mount, minimal },
});
}
updateServiceConfiguration(mount, cfg, minimal = false) {
return this.request({
method: "PATCH",
pathname: "/_api/foxx/configuration",
body: cfg,
search: { mount, minimal },
});
}
getServiceDependencies(mount, minimal = false) {
return this.request({
pathname: "/_api/foxx/dependencies",
search: { mount, minimal },
});
}
replaceServiceDependencies(mount, deps, minimal = false) {
return this.request({
method: "PUT",
pathname: "/_api/foxx/dependencies",
body: deps,
search: { mount, minimal },
});
}
updateServiceDependencies(mount, deps, minimal = false) {
return this.request({
method: "PATCH",
pathname: "/_api/foxx/dependencies",
body: deps,
search: { mount, minimal },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Enables or disables development mode for the given service.
*
* @param mount - The service's mount point, relative to the database.
* @param enabled - Whether development mode should be enabled or disabled.
*
* @example
* ```js
* const db = new Database();
* await db.setServiceDevelopmentMode("/my-service", true);
* // the service is now in development mode
* await db.setServiceDevelopmentMode("/my-service", false);
* // the service is now in production mode
* ```
*/
setServiceDevelopmentMode(mount, enabled = true) {
return this.request({
method: enabled ? "POST" : "DELETE",
pathname: "/_api/foxx/development",
search: { mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Retrieves an object mapping script names to their human readable
* representations, as defined in the service manifest's "scripts" section.
*
* @param mount - The service's mount point, relative to the database.
*
* @example
* ```js
* const db = new Database();
* const scripts = await db.getServiceScripts("/my-service");
* for (const [name, title] of Object.entries(scripts)) {
* console.log(`${name}: ${title}`);
* }
* ```
*/
getServiceScripts(mount) {
return this.request({
pathname: "/_api/foxx/scripts",
search: { mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Executes a service script and retrieves its result exposed as
* `module.exports` (if any).
*
* @param mount - The service's mount point, relative to the database.
* @param name - Name of the service script to execute as defined in the
* service manifest.
* @param params - Arbitrary value that will be exposed to the script as
* `argv[0]` in the service context (e.g. `module.context.argv[0]`).
* Must be serializable to JSON.
*
* @example
* ```js
* const db = new Database();
* const result = await db.runServiceScript(
* "/my-service",
* "create-user",
* {
* username: "service_admin",
* password: "hunter2"
* }
* );
* ```
*/
runServiceScript(mount, name, params) {
return this.request({
method: "POST",
pathname: `/_api/foxx/scripts/${encodeURIComponent(name)}`,
body: params,
search: { mount },
});
}
runServiceTests(mount, options) {
return this.request({
method: "POST",
pathname: "/_api/foxx/tests",
search: {
...options,
mount,
},
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Retrieves the text content of the service's `README` or `README.md` file.
*
* Returns `undefined` if no such file could be found.
*
* @param mount - The service's mount point, relative to the database.
*
* @example
* ```js
* const db = new Database();
* const readme = await db.getServiceReadme("/my-service");
* if (readme !== undefined) console.log(readme);
* else console.warn(`No README found.`)
* ```
*/
getServiceReadme(mount) {
return this.request({
pathname: "/_api/foxx/readme",
search: { mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Retrieves an Open API compatible Swagger API description object for the
* service installed at the given mount point.
*
* @param mount - The service's mount point, relative to the database.
*
* @example
* ```js
* const db = new Database();
* const spec = await db.getServiceDocumentation("/my-service");
* // spec is a Swagger API description of the service
* ```
*/
getServiceDocumentation(mount) {
return this.request({
pathname: "/_api/foxx/swagger",
search: { mount },
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Retrieves a zip bundle containing the service files.
*
* Returns a `Buffer` in node.js or `Blob` in the browser.
*
* @param mount - The service's mount point, relative to the database.
*
* @example
* ```js
* const db = new Database();
* const serviceBundle = await db.downloadService("/my-foxx");
* ```
*/
downloadService(mount) {
return this.request({
method: "POST",
pathname: "/_api/foxx/download",
search: { mount },
expectBinary: true,
});
}
/**
* @deprecated Removed from ArangoDB 4.0 onwards.
* Calling this method against ArangoDB v4 will result in a runtime exception.
*
* Writes all locally available services to the database and updates any
* service bundles missing in the database.
*
* @param replace - If set to `true`, outdated services will also be
* committed. This can be used to solve some consistency problems when
* service bundles are missing in the database or were deleted manually.
*
* @example
* ```js
* await db.commitLocalServiceState();
* // all services available on the coordinator have been written to the db
* ```
*
* @example
* ```js
* await db.commitLocalServiceState(true);
* // all service conflicts have been resolved in favor of this coordinator
* ```
*/
commitLocalServiceState(replace = false) {
return this.request({
method: "POST",
pathname: "/_api/foxx/commit",
search: { replace },
}, () => undefined);
}
//#endregion
//#region hot backups
/**
* (Enterprise Edition only.) Creates a hot backup of the entire ArangoDB
* deployment including all databases, collections, etc.
*
* Returns an object describing the backup result.
*
* @param options - Options for creating the backup.
*
* @example
* ```js
* const info = await db.createHotBackup();
* // a hot backup has been created
* ```
*/
createHotBackup(options = {}) {
return this.request({
method: "POST",
pathname: "/_admin/backup/create",
body: options,
}, (res) => res.parsedBody.result);
}
/**
* (Enterprise Edition only.) Retrieves a list of all locally found hot
* backups.
*
* @param id - If specified, only the backup with the given ID will be
* returned.
*
* @example
* ```js
* const backups = await db.getHotBackups();
* for (const backup of backups.list) {
* console.log(backup.id);
* }
* ```
*/
getHotBackups(id) {
return this.request({
method: "POST",
pathname: "/_admin/backup/list",
body: id ? { id } : undefined,
}, (res) => res.parsedBody.result);
}
/**
* (Enteprise Edition only.) Restores a consistent local hot backup.
*
* Returns the directory path of the restored backup.
*
* @param id - The ID of the backup to restore.
*
* @example
* ```js
* await db.restoreHotBackup("2023-09-19T15.38.21Z_example");
* // the backup has been restored
* ```
*/
restoreHotBackup(id) {
return this.request({
method: "POST",
pathname: "/_admin/backup/restore",
body: { id },
}, (res) => res.parsedBody.result.previous);
}
/**
* (Enterprise Edition only.) Deletes a local hot backup.
*
* @param id - The ID of the backup to delete.
*
* @example
* ```js
* await db.deleteHotBackup("2023-09-19T15.38.21Z_example");
* // the backup has been deleted
* ```
*/
deleteHotBackup(id) {
return this.request({
method: "POST",
pathname: "/_admin/backup/delete",
body: { id },
}, () => undefined);
}
//#endregion
//#region logs
/**
* Retrieves the log messages from the server's global log.
*
* @param options - Options for retrieving the log entries.
*
* @example
* ```js
* const log = await db.getLogEntries();
* for (let i = 0; i < log.totalAmount; i++) {
* console.log(`${
* new Date(log.timestamp[i] * 1000).toISOString()
* } - [${LogLevel[log.level[i]]}] ${log.text[i]} (#${log.lid[i]})`);
* }
* ```
*/
getLogEntries(options) {
return this.request({
pathname: "/_admin/log/entries",
search: options,
}, (res) => res.parsedBody);
}
/**
* Retrieves the log messages from the server's global log.
*
* @param options - Options for retrieving the log entries.
*
* @deprecated This endpoint has been deprecated in ArangoDB 3.8.
* Use {@link Database#getLogEntries} instead.
*
* @example
* ```js
* const messages = await db.listLogMessages();
* for (const m of messages) {
* console.log(`${m.date} - [${m.level}] ${m.message} (#${m.id})`);
* }
* ```
*/
listLogMessages(options) {
return this.request({
pathname: "/_admin/log",
search: options,
}, (res) => res.parsedBody.messages);
}
/**
* Retrieves the server's current log level for each topic.
*
* @example
* ```js
* const levels = await db.getLogLevel();
* console.log(levels.request); // log level for incoming requests
* ```
*/
getLogLevel() {
return this.request({
pathname: "/_admin/log/level",
});
}
/**
* Sets the server's log level for each of the given topics to the given level.
*
* Any omitted topics will be left unchanged.
*
* @param levels - An object mapping topic names to log levels.
*
* @example
* ```js
* await db.setLogLevel({ request: "debug" });
* // Debug information will now be logged for each request
* ```
*/
setLogLevel(levels) {
return this.request({
method: "PUT",
pathname: "/_admin/log/level",
body: levels,
});
}
//#endregion
//#region async jobs
/**
* 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 jobs.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 jobs.Job(this, jobId, (res) => {
onResolve(res);
return eventualResult;
}, (e) => {
onReject(e);
return eventualResult;
});
}
/**
* Returns a {@link jobs.Job} instance for the given `jobId`.
*
* @param jobId - ID of the async job.
*
* @example
* ```js
* const db = new Database();
* const job = db.job("12345");
* ```
*/
job(jobId) {
return new jobs.Job(this, jobId);
}
/**
* Returns a list of the IDs of all currently pending async jobs.
*
* @example
* ```js
* const db = new Database();
* const pendingJobs = await db.listPendingJobs();
* console.log(pendingJobs); // e.g. ["12345", "67890"]
* ```
*/
listPendingJobs() {
return this.request({
pathname: "/_api/job/pending",
}, (res) => res.parsedBody);
}
/**
* Returns a list of the IDs of all currently available completed async jobs.
*
* @example
* ```js
* const db = new Database();
* const completedJobs = await db.listCompletedJobs();
* console.log(completedJobs); // e.g. ["12345", "67890"]
* ```
*/
listCompletedJobs() {
return this.request({
pathname: "/_api/job/done",
}, (res) => res.parsedBody);
}
/**
* Deletes the results of all completed async jobs created before the given
* threshold.
*
* @param threshold - The expiration timestamp in milliseconds.
*
* @example
* ```js
* const db = new Database();
* const ONE_WEEK = 7 * 24 * 60 * 60 * 1000;
* await db.deleteExpiredJobResults(Date.now() - ONE_WEEK);
* // all job results older than a week have been deleted
* ```
*/
deleteExpiredJobResults(threshold) {
return this.request({
method: "DELETE",
pathname: `/_api/job/expired`,
search: { stamp: threshold / 1000 },
}, () => undefined);
}
/**
* Deletes the results of all completed async jobs.
*/
deleteAllJobResults() {
return this.request({
method: "DELETE",
pathname: `/_api/job/all`,
}, () => undefined);
}
}
exports.Database = Database;
//#endregion
//# sourceMappingURL=databases.js.map