1__all__ = ["StandardDatabase", "AsyncDatabase", "BatchDatabase", "TransactionDatabase"]
2
3from datetime import datetime
4from numbers import Number
5from typing import Any, List, Optional, Sequence, Union
6
7from arango.api import ApiGroup
8from arango.aql import AQL
9from arango.backup import Backup
10from arango.cluster import Cluster
11from arango.collection import StandardCollection
12from arango.connection import Connection
13from arango.exceptions import (
14    AnalyzerCreateError,
15    AnalyzerDeleteError,
16    AnalyzerGetError,
17    AnalyzerListError,
18    AsyncJobClearError,
19    AsyncJobListError,
20    CollectionCreateError,
21    CollectionDeleteError,
22    CollectionListError,
23    DatabaseCreateError,
24    DatabaseDeleteError,
25    DatabaseListError,
26    DatabasePropertiesError,
27    GraphCreateError,
28    GraphDeleteError,
29    GraphListError,
30    JWTSecretListError,
31    JWTSecretReloadError,
32    PermissionGetError,
33    PermissionListError,
34    PermissionResetError,
35    PermissionUpdateError,
36    ServerDetailsError,
37    ServerEchoError,
38    ServerEncryptionError,
39    ServerEngineError,
40    ServerLogLevelError,
41    ServerLogLevelSetError,
42    ServerMetricsError,
43    ServerReadLogError,
44    ServerReloadRoutingError,
45    ServerRequiredDBVersionError,
46    ServerRoleError,
47    ServerRunTestsError,
48    ServerShutdownError,
49    ServerStatisticsError,
50    ServerStatusError,
51    ServerTimeError,
52    ServerTLSError,
53    ServerTLSReloadError,
54    ServerVersionError,
55    TaskCreateError,
56    TaskDeleteError,
57    TaskGetError,
58    TaskListError,
59    TransactionExecuteError,
60    UserCreateError,
61    UserDeleteError,
62    UserGetError,
63    UserListError,
64    UserReplaceError,
65    UserUpdateError,
66    ViewCreateError,
67    ViewDeleteError,
68    ViewGetError,
69    ViewListError,
70    ViewRenameError,
71    ViewReplaceError,
72    ViewUpdateError,
73)
74from arango.executor import (
75    AsyncApiExecutor,
76    BatchApiExecutor,
77    DefaultApiExecutor,
78    TransactionApiExecutor,
79)
80from arango.formatter import (
81    format_body,
82    format_database,
83    format_server_status,
84    format_tls,
85    format_view,
86)
87from arango.foxx import Foxx
88from arango.graph import Graph
89from arango.job import BatchJob
90from arango.pregel import Pregel
91from arango.replication import Replication
92from arango.request import Request
93from arango.response import Response
94from arango.result import Result
95from arango.typings import Json, Jsons, Params
96from arango.utils import get_col_name
97from arango.wal import WAL
98
99
100class Database(ApiGroup):
101    """Base class for Database API wrappers."""
102
103    def __getitem__(self, name: str) -> StandardCollection:
104        """Return the collection API wrapper.
105
106        :param name: Collection name.
107        :type name: str
108        :return: Collection API wrapper.
109        :rtype: arango.collection.StandardCollection
110        """
111        return self.collection(name)
112
113    def _get_col_by_doc(self, document: Union[str, Json]) -> StandardCollection:
114        """Return the collection of the given document.
115
116        :param document: Document ID or body with "_id" field.
117        :type document: str | dict
118        :return: Collection API wrapper.
119        :rtype: arango.collection.StandardCollection
120        :raise arango.exceptions.DocumentParseError: On malformed document.
121        """
122        return self.collection(get_col_name(document))
123
124    @property
125    def name(self) -> str:
126        """Return database name.
127
128        :return: Database name.
129        :rtype: str
130        """
131        return self.db_name
132
133    @property
134    def aql(self) -> AQL:
135        """Return AQL (ArangoDB Query Language) API wrapper.
136
137        :return: AQL API wrapper.
138        :rtype: arango.aql.AQL
139        """
140        return AQL(self._conn, self._executor)
141
142    @property
143    def wal(self) -> WAL:
144        """Return WAL (Write-Ahead Log) API wrapper.
145
146        :return: WAL API wrapper.
147        :rtype: arango.wal.WAL
148        """
149        return WAL(self._conn, self._executor)
150
151    @property
152    def foxx(self) -> Foxx:
153        """Return Foxx API wrapper.
154
155        :return: Foxx API wrapper.
156        :rtype: arango.foxx.Foxx
157        """
158        return Foxx(self._conn, self._executor)
159
160    @property
161    def pregel(self) -> Pregel:
162        """Return Pregel API wrapper.
163
164        :return: Pregel API wrapper.
165        :rtype: arango.pregel.Pregel
166        """
167        return Pregel(self._conn, self._executor)
168
169    @property
170    def replication(self) -> Replication:
171        """Return Replication API wrapper.
172
173        :return: Replication API wrapper.
174        :rtype: arango.replication.Replication
175        """
176        return Replication(self._conn, self._executor)
177
178    @property
179    def cluster(self) -> Cluster:  # pragma: no cover
180        """Return Cluster API wrapper.
181
182        :return: Cluster API wrapper.
183        :rtype: arango.cluster.Cluster
184        """
185        return Cluster(self._conn, self._executor)
186
187    @property
188    def backup(self) -> Backup:
189        """Return Backup API wrapper.
190
191        :return: Backup API wrapper.
192        :rtype: arango.backup.Backup
193        """
194        return Backup(self._conn, self._executor)
195
196    def properties(self) -> Result[Json]:
197        """Return database properties.
198
199        :return: Database properties.
200        :rtype: dict
201        :raise arango.exceptions.DatabasePropertiesError: If retrieval fails.
202        """
203        request = Request(
204            method="get",
205            endpoint="/_api/database/current",
206        )
207
208        def response_handler(resp: Response) -> Json:
209            if not resp.is_success:
210                raise DatabasePropertiesError(resp, request)
211            return format_database(resp.body["result"])
212
213        return self._execute(request, response_handler)
214
215    def execute_transaction(
216        self,
217        command: str,
218        params: Optional[Json] = None,
219        read: Optional[Sequence[str]] = None,
220        write: Optional[Sequence[str]] = None,
221        sync: Optional[bool] = None,
222        timeout: Optional[Number] = None,
223        max_size: Optional[int] = None,
224        allow_implicit: Optional[bool] = None,
225        intermediate_commit_count: Optional[int] = None,
226        intermediate_commit_size: Optional[int] = None,
227    ) -> Result[Any]:
228        """Execute raw Javascript command in transaction.
229
230        :param command: Javascript command to execute.
231        :type command: str
232        :param read: Names of collections read during transaction. If parameter
233            **allow_implicit** is set to True, any undeclared read collections
234            are loaded lazily.
235        :type read: [str] | None
236        :param write: Names of collections written to during transaction.
237            Transaction fails on undeclared write collections.
238        :type write: [str] | None
239        :param params: Optional parameters passed into the Javascript command.
240        :type params: dict | None
241        :param sync: Block until operation is synchronized to disk.
242        :type sync: bool | None
243        :param timeout: Timeout for waiting on collection locks. If set to 0,
244            ArangoDB server waits indefinitely. If not set, system default
245            value is used.
246        :type timeout: int | None
247        :param max_size: Max transaction size limit in bytes.
248        :type max_size: int | None
249        :param allow_implicit: If set to True, undeclared read collections are
250            loaded lazily. If set to False, transaction fails on any undeclared
251            collections.
252        :type allow_implicit: bool | None
253        :param intermediate_commit_count: Max number of operations after which
254            an intermediate commit is performed automatically.
255        :type intermediate_commit_count: int | None
256        :param intermediate_commit_size: Max size of operations in bytes after
257            which an intermediate commit is performed automatically.
258        :type intermediate_commit_size: int | None
259        :return: Return value of **command**.
260        :rtype: Any
261        :raise arango.exceptions.TransactionExecuteError: If execution fails.
262        """
263        collections: Json = {"allowImplicit": allow_implicit}
264        if read is not None:
265            collections["read"] = read
266        if write is not None:
267            collections["write"] = write
268
269        data: Json = {"action": command}
270        if collections:
271            data["collections"] = collections
272        if params is not None:
273            data["params"] = params
274        if timeout is not None:
275            data["lockTimeout"] = timeout
276        if sync is not None:
277            data["waitForSync"] = sync
278        if max_size is not None:
279            data["maxTransactionSize"] = max_size
280        if intermediate_commit_count is not None:
281            data["intermediateCommitCount"] = intermediate_commit_count
282        if intermediate_commit_size is not None:
283            data["intermediateCommitSize"] = intermediate_commit_size
284
285        request = Request(method="post", endpoint="/_api/transaction", data=data)
286
287        def response_handler(resp: Response) -> Any:
288            if not resp.is_success:
289                raise TransactionExecuteError(resp, request)
290
291            return resp.body.get("result")
292
293        return self._execute(request, response_handler)
294
295    def version(self) -> Result[str]:
296        """Return ArangoDB server version.
297
298        :return: Server version.
299        :rtype: str
300        :raise arango.exceptions.ServerVersionError: If retrieval fails.
301        """
302        request = Request(
303            method="get", endpoint="/_api/version", params={"details": False}
304        )
305
306        def response_handler(resp: Response) -> str:
307            if not resp.is_success:
308                raise ServerVersionError(resp, request)
309            return str(resp.body["version"])
310
311        return self._execute(request, response_handler)
312
313    def details(self) -> Result[Json]:
314        """Return ArangoDB server details.
315
316        :return: Server details.
317        :rtype: dict
318        :raise arango.exceptions.ServerDetailsError: If retrieval fails.
319        """
320        request = Request(
321            method="get", endpoint="/_api/version", params={"details": True}
322        )
323
324        def response_handler(resp: Response) -> Json:
325            if resp.is_success:
326                result: Json = resp.body["details"]
327                return result
328            raise ServerDetailsError(resp, request)
329
330        return self._execute(request, response_handler)
331
332    def status(self) -> Result[Json]:
333        """Return ArangoDB server status.
334
335        :return: Server status.
336        :rtype: dict
337        :raise arango.exceptions.ServerStatusError: If retrieval fails.
338        """
339        request = Request(
340            method="get",
341            endpoint="/_admin/status",
342        )
343
344        def response_handler(resp: Response) -> Json:
345            if not resp.is_success:
346                raise ServerStatusError(resp, request)
347            return format_server_status(resp.body)
348
349        return self._execute(request, response_handler)
350
351    def required_db_version(self) -> Result[str]:
352        """Return required version of target database.
353
354        :return: Required version of target database.
355        :rtype: str
356        :raise arango.exceptions.ServerRequiredDBVersionError: If retrieval fails.
357        """
358        request = Request(method="get", endpoint="/_admin/database/target-version")
359
360        def response_handler(resp: Response) -> str:
361            if resp.is_success:
362                return str(resp.body["version"])
363            raise ServerRequiredDBVersionError(resp, request)
364
365        return self._execute(request, response_handler)
366
367    def engine(self) -> Result[Json]:
368        """Return the database engine details.
369
370        :return: Database engine details.
371        :rtype: dict
372        :raise arango.exceptions.ServerEngineError: If retrieval fails.
373        """
374        request = Request(method="get", endpoint="/_api/engine")
375
376        def response_handler(resp: Response) -> Json:
377            if resp.is_success:
378                return format_body(resp.body)
379            raise ServerEngineError(resp, request)
380
381        return self._execute(request, response_handler)
382
383    def statistics(self, description: bool = False) -> Result[Json]:
384        """Return server statistics.
385
386        :return: Server statistics.
387        :rtype: dict
388        :raise arango.exceptions.ServerStatisticsError: If retrieval fails.
389        """
390        if description:
391            endpoint = "/_admin/statistics-description"
392        else:
393            endpoint = "/_admin/statistics"
394
395        request = Request(method="get", endpoint=endpoint)
396
397        def response_handler(resp: Response) -> Json:
398            if resp.is_success:
399                return format_body(resp.body)
400            raise ServerStatisticsError(resp, request)
401
402        return self._execute(request, response_handler)
403
404    def role(self) -> Result[str]:
405        """Return server role.
406
407        :return: Server role. Possible values are "SINGLE" (server which is not
408            in a cluster), "COORDINATOR" (cluster coordinator), "PRIMARY",
409            "SECONDARY", "AGENT" (Agency node in a cluster) or "UNDEFINED".
410        :rtype: str
411        :raise arango.exceptions.ServerRoleError: If retrieval fails.
412        """
413        request = Request(method="get", endpoint="/_admin/server/role")
414
415        def response_handler(resp: Response) -> str:
416            if resp.is_success:
417                return str(resp.body["role"])
418            raise ServerRoleError(resp, request)
419
420        return self._execute(request, response_handler)
421
422    def time(self) -> Result[datetime]:
423        """Return server system time.
424
425        :return: Server system time.
426        :rtype: datetime.datetime
427        :raise arango.exceptions.ServerTimeError: If retrieval fails.
428        """
429        request = Request(method="get", endpoint="/_admin/time")
430
431        def response_handler(resp: Response) -> datetime:
432            if not resp.is_success:
433                raise ServerTimeError(resp, request)
434            return datetime.fromtimestamp(resp.body["time"])
435
436        return self._execute(request, response_handler)
437
438    def echo(self) -> Result[Json]:
439        """Return details of the last request (e.g. headers, payload).
440
441        :return: Details of the last request.
442        :rtype: dict
443        :raise arango.exceptions.ServerEchoError: If retrieval fails.
444        """
445        request = Request(method="get", endpoint="/_admin/echo")
446
447        def response_handler(resp: Response) -> Json:
448            if not resp.is_success:
449                raise ServerEchoError(resp, request)
450            result: Json = resp.body
451            return result
452
453        return self._execute(request, response_handler)
454
455    def shutdown(self) -> Result[bool]:  # pragma: no cover
456        """Initiate server shutdown sequence.
457
458        :return: True if the server was shutdown successfully.
459        :rtype: bool
460        :raise arango.exceptions.ServerShutdownError: If shutdown fails.
461        """
462        request = Request(method="delete", endpoint="/_admin/shutdown")
463
464        def response_handler(resp: Response) -> bool:
465            if not resp.is_success:
466                raise ServerShutdownError(resp, request)
467            return True
468
469        return self._execute(request, response_handler)
470
471    def run_tests(self, tests: Sequence[str]) -> Result[Json]:  # pragma: no cover
472        """Run available unittests on the server.
473
474        :param tests: List of files containing the test suites.
475        :type tests: [str]
476        :return: Test results.
477        :rtype: dict
478        :raise arango.exceptions.ServerRunTestsError: If execution fails.
479        """
480        request = Request(method="post", endpoint="/_admin/test", data={"tests": tests})
481
482        def response_handler(resp: Response) -> Json:
483            if not resp.is_success:
484                raise ServerRunTestsError(resp, request)
485            result: Json = resp.body
486            return result
487
488        return self._execute(request, response_handler)
489
490    def read_log(
491        self,
492        upto: Optional[Union[int, str]] = None,
493        level: Optional[Union[int, str]] = None,
494        start: Optional[int] = None,
495        size: Optional[int] = None,
496        offset: Optional[int] = None,
497        search: Optional[str] = None,
498        sort: Optional[str] = None,
499    ) -> Result[Json]:
500        """Read the global log from server.
501
502        :param upto: Return the log entries up to the given level (mutually
503            exclusive with parameter **level**). Allowed values are "fatal",
504            "error", "warning", "info" (default) and "debug".
505        :type upto: int | str
506        :param level: Return the log entries of only the given level (mutually
507            exclusive with **upto**). Allowed values are "fatal", "error",
508            "warning", "info" (default) and "debug".
509        :type level: int | str
510        :param start: Return the log entries whose ID is greater or equal to
511            the given value.
512        :type start: int
513        :param size: Restrict the size of the result to the given value. This
514            can be used for pagination.
515        :type size: int
516        :param offset: Number of entries to skip (e.g. for pagination).
517        :type offset: int
518        :param search: Return only the log entries containing the given text.
519        :type search: str
520        :param sort: Sort the log entries according to the given fashion, which
521            can be "sort" or "desc".
522        :type sort: str
523        :return: Server log entries.
524        :rtype: dict
525        :raise arango.exceptions.ServerReadLogError: If read fails.
526        """
527        params = dict()
528        if upto is not None:
529            params["upto"] = upto
530        if level is not None:
531            params["level"] = level
532        if start is not None:
533            params["start"] = start
534        if size is not None:
535            params["size"] = size
536        if offset is not None:
537            params["offset"] = offset
538        if search is not None:
539            params["search"] = search
540        if sort is not None:
541            params["sort"] = sort
542
543        request = Request(method="get", endpoint="/_admin/log", params=params)
544
545        def response_handler(resp: Response) -> Json:
546            if not resp.is_success:
547                raise ServerReadLogError(resp, request)
548
549            result: Json = resp.body
550            if "totalAmount" in result:
551                resp.body["total_amount"] = resp.body.pop("totalAmount")
552            return result
553
554        return self._execute(request, response_handler)
555
556    def log_levels(self) -> Result[Json]:
557        """Return current logging levels.
558
559        :return: Current logging levels.
560        :rtype: dict
561        """
562        request = Request(method="get", endpoint="/_admin/log/level")
563
564        def response_handler(resp: Response) -> Json:
565            if not resp.is_success:
566                raise ServerLogLevelError(resp, request)
567            result: Json = resp.body
568            return result
569
570        return self._execute(request, response_handler)
571
572    def set_log_levels(self, **kwargs: str) -> Result[Json]:
573        """Set the logging levels.
574
575        This method takes arbitrary keyword arguments where the keys are the
576        logger names and the values are the logging levels. For example:
577
578        .. code-block:: python
579
580            arango.set_log_levels(
581                agency='DEBUG',
582                collector='INFO',
583                threads='WARNING'
584            )
585
586        Keys that are not valid logger names are ignored.
587
588        :return: New logging levels.
589        :rtype: dict
590        """
591        request = Request(method="put", endpoint="/_admin/log/level", data=kwargs)
592
593        def response_handler(resp: Response) -> Json:
594            if not resp.is_success:
595                raise ServerLogLevelSetError(resp, request)
596            result: Json = resp.body
597            return result
598
599        return self._execute(request, response_handler)
600
601    def reload_routing(self) -> Result[bool]:
602        """Reload the routing information.
603
604        :return: True if routing was reloaded successfully.
605        :rtype: bool
606        :raise arango.exceptions.ServerReloadRoutingError: If reload fails.
607        """
608        request = Request(method="post", endpoint="/_admin/routing/reload")
609
610        def response_handler(resp: Response) -> bool:
611            if not resp.is_success:
612                raise ServerReloadRoutingError(resp, request)
613            return True
614
615        return self._execute(request, response_handler)
616
617    def metrics(self) -> Result[str]:
618        """Return server metrics in Prometheus format.
619
620        :return: Server metrics in Prometheus format.
621        :rtype: str
622        """
623        request = Request(method="get", endpoint="/_admin/metrics")
624
625        def response_handler(resp: Response) -> str:
626            if resp.is_success:
627                return resp.raw_body
628            raise ServerMetricsError(resp, request)
629
630        return self._execute(request, response_handler)
631
632    def jwt_secrets(self) -> Result[Json]:  # pragma: no cover
633        """Return information on currently loaded JWT secrets.
634
635        :return: Information on currently loaded JWT secrets.
636        :rtype: dict
637        """
638        request = Request(method="get", endpoint="/_admin/server/jwt")
639
640        def response_handler(resp: Response) -> Json:
641            if not resp.is_success:
642                raise JWTSecretListError(resp, request)
643            result: Json = resp.body["result"]
644            return result
645
646        return self._execute(request, response_handler)
647
648    def reload_jwt_secrets(self) -> Result[Json]:  # pragma: no cover
649        """Hot-reload JWT secrets.
650
651        Calling this without payload reloads JWT secrets from disk. Only files
652        specified via arangod startup option ``--server.jwt-secret-keyfile`` or
653        ``--server.jwt-secret-folder`` are used. It is not possible to change
654        the location where files are loaded from without restarting the server.
655
656        :return: Information on reloaded JWT secrets.
657        :rtype: dict
658        """
659        request = Request(method="post", endpoint="/_admin/server/jwt")
660
661        def response_handler(resp: Response) -> Json:
662            if not resp.is_success:
663                raise JWTSecretReloadError(resp, request)
664            result: Json = resp.body["result"]
665            return result
666
667        return self._execute(request, response_handler)
668
669    def tls(self) -> Result[Json]:
670        """Return TLS data (server key, client-auth CA).
671
672        :return: TLS data.
673        :rtype: dict
674        """
675        request = Request(method="get", endpoint="/_admin/server/tls")
676
677        def response_handler(resp: Response) -> Json:
678            if not resp.is_success:
679                raise ServerTLSError(resp, request)
680            return format_tls(resp.body["result"])
681
682        return self._execute(request, response_handler)
683
684    def reload_tls(self) -> Result[Json]:
685        """Reload TLS data (server key, client-auth CA).
686
687        :return: New TLS data.
688        :rtype: dict
689        """
690        request = Request(method="post", endpoint="/_admin/server/tls")
691
692        def response_handler(resp: Response) -> Json:
693            if not resp.is_success:
694                raise ServerTLSReloadError(resp, request)
695            return format_tls(resp.body["result"])
696
697        return self._execute(request, response_handler)
698
699    def encryption(self) -> Result[Json]:
700        """Rotate the user-supplied keys for encryption.
701
702        This method is available only for enterprise edition of ArangoDB.
703
704        :return: New TLS data.
705        :rtype: dict
706        :raise arango.exceptions.ServerEncryptionError: If retrieval fails.
707        """
708        request = Request(method="post", endpoint="/_admin/server/encryption")
709
710        def response_handler(resp: Response) -> Json:
711            if resp.is_success:  # pragma: no cover
712                result: Json = resp.body["result"]
713                return result
714            raise ServerEncryptionError(resp, request)
715
716        return self._execute(request, response_handler)
717
718    #######################
719    # Database Management #
720    #######################
721
722    def databases(self) -> Result[List[str]]:
723        """Return the names all databases.
724
725        :return: Database names.
726        :rtype: [str]
727        :raise arango.exceptions.DatabaseListError: If retrieval fails.
728        """
729        request = Request(method="get", endpoint="/_api/database")
730
731        def response_handler(resp: Response) -> List[str]:
732            if not resp.is_success:
733                raise DatabaseListError(resp, request)
734            result: List[str] = resp.body["result"]
735            return result
736
737        return self._execute(request, response_handler)
738
739    def has_database(self, name: str) -> Result[bool]:
740        """Check if a database exists.
741
742        :param name: Database name.
743        :type name: str
744        :return: True if database exists, False otherwise.
745        :rtype: bool
746        """
747        request = Request(method="get", endpoint="/_api/database")
748
749        def response_handler(resp: Response) -> bool:
750            if not resp.is_success:
751                raise DatabaseListError(resp, request)
752            return name in resp.body["result"]
753
754        return self._execute(request, response_handler)
755
756    def create_database(
757        self,
758        name: str,
759        users: Optional[Sequence[Json]] = None,
760        replication_factor: Union[int, str, None] = None,
761        write_concern: Optional[int] = None,
762        sharding: Optional[str] = None,
763    ) -> Result[bool]:
764        """Create a new database.
765
766        :param name: Database name.
767        :type name: str
768        :param users: List of users with access to the new database, where each
769            user is a dictionary with fields "username", "password", "active"
770            and "extra" (see below for example). If not set, only the admin and
771            current user are granted access.
772        :type users: [dict]
773        :param replication_factor: Default replication factor for collections
774            created in this database. Special values include "satellite" which
775            replicates the collection to every DBServer, and 1 which disables
776            replication. Used for clusters only.
777        :type replication_factor: int | str
778        :param write_concern: Default write concern for collections created in
779            this database. Determines how many copies of each shard are
780            required to be in sync on different DBServers. If there are less
781            than these many copies in the cluster a shard will refuse to write.
782            Writes to shards with enough up-to-date copies will succeed at the
783            same time, however. Value of this parameter can not be larger than
784            the value of **replication_factor**. Used for clusters only.
785        :type write_concern: int
786        :param sharding: Sharding method used for new collections in this
787            database. Allowed values are: "", "flexible" and "single". The
788            first two are equivalent. Used for clusters only.
789        :type sharding: str
790        :return: True if database was created successfully.
791        :rtype: bool
792        :raise arango.exceptions.DatabaseCreateError: If create fails.
793
794        Here is an example entry for parameter **users**:
795
796        .. code-block:: python
797
798            {
799                'username': 'john',
800                'password': 'password',
801                'active': True,
802                'extra': {'Department': 'IT'}
803            }
804        """
805        data: Json = {"name": name}
806
807        options: Json = {}
808        if replication_factor is not None:
809            options["replicationFactor"] = replication_factor
810        if write_concern is not None:
811            options["writeConcern"] = write_concern
812        if sharding is not None:
813            options["sharding"] = sharding
814        if options:
815            data["options"] = options
816
817        if users is not None:
818            data["users"] = [
819                {
820                    "username": user["username"],
821                    "passwd": user["password"],
822                    "active": user.get("active", True),
823                    "extra": user.get("extra", {}),
824                }
825                for user in users
826            ]
827
828        request = Request(method="post", endpoint="/_api/database", data=data)
829
830        def response_handler(resp: Response) -> bool:
831            if not resp.is_success:
832                raise DatabaseCreateError(resp, request)
833            return True
834
835        return self._execute(request, response_handler)
836
837    def delete_database(self, name: str, ignore_missing: bool = False) -> Result[bool]:
838        """Delete the database.
839
840        :param name: Database name.
841        :type name: str
842        :param ignore_missing: Do not raise an exception on missing database.
843        :type ignore_missing: bool
844        :return: True if database was deleted successfully, False if database
845            was not found and **ignore_missing** was set to True.
846        :rtype: bool
847        :raise arango.exceptions.DatabaseDeleteError: If delete fails.
848        """
849        request = Request(method="delete", endpoint=f"/_api/database/{name}")
850
851        def response_handler(resp: Response) -> bool:
852            if resp.error_code == 1228 and ignore_missing:
853                return False
854            if not resp.is_success:
855                raise DatabaseDeleteError(resp, request)
856            return True
857
858        return self._execute(request, response_handler)
859
860    #########################
861    # Collection Management #
862    #########################
863
864    def collection(self, name: str) -> StandardCollection:
865        """Return the standard collection API wrapper.
866
867        :param name: Collection name.
868        :type name: str
869        :return: Standard collection API wrapper.
870        :rtype: arango.collection.StandardCollection
871        """
872        return StandardCollection(self._conn, self._executor, name)
873
874    def has_collection(self, name: str) -> Result[bool]:
875        """Check if collection exists in the database.
876
877        :param name: Collection name.
878        :type name: str
879        :return: True if collection exists, False otherwise.
880        :rtype: bool
881        """
882        request = Request(method="get", endpoint="/_api/collection")
883
884        def response_handler(resp: Response) -> bool:
885            if not resp.is_success:
886                raise CollectionListError(resp, request)
887            return any(col["name"] == name for col in resp.body["result"])
888
889        return self._execute(request, response_handler)
890
891    def collections(self) -> Result[Jsons]:
892        """Return the collections in the database.
893
894        :return: Collections in the database and their details.
895        :rtype: [dict]
896        :raise arango.exceptions.CollectionListError: If retrieval fails.
897        """
898        request = Request(method="get", endpoint="/_api/collection")
899
900        def response_handler(resp: Response) -> Jsons:
901            if not resp.is_success:
902                raise CollectionListError(resp, request)
903            return [
904                {
905                    "id": col["id"],
906                    "name": col["name"],
907                    "system": col["isSystem"],
908                    "type": StandardCollection.types[col["type"]],
909                    "status": StandardCollection.statuses[col["status"]],
910                }
911                for col in resp.body["result"]
912            ]
913
914        return self._execute(request, response_handler)
915
916    def create_collection(
917        self,
918        name: str,
919        sync: bool = False,
920        system: bool = False,
921        edge: bool = False,
922        user_keys: bool = True,
923        key_increment: Optional[int] = None,
924        key_offset: Optional[int] = None,
925        key_generator: str = "traditional",
926        shard_fields: Optional[Sequence[str]] = None,
927        shard_count: Optional[int] = None,
928        replication_factor: Optional[int] = None,
929        shard_like: Optional[str] = None,
930        sync_replication: Optional[bool] = None,
931        enforce_replication_factor: Optional[bool] = None,
932        sharding_strategy: Optional[str] = None,
933        smart_join_attribute: Optional[str] = None,
934        write_concern: Optional[int] = None,
935        schema: Optional[Json] = None,
936    ) -> Result[StandardCollection]:
937        """Create a new collection.
938
939        :param name: Collection name.
940        :type name: str
941        :param sync: If set to True, document operations via the collection
942            will block until synchronized to disk by default.
943        :type sync: bool | None
944        :param system: If set to True, a system collection is created. The
945            collection name must have leading underscore "_" character.
946        :type system: bool
947        :param edge: If set to True, an edge collection is created.
948        :type edge: bool
949        :param key_generator: Used for generating document keys. Allowed values
950            are "traditional" or "autoincrement".
951        :type key_generator: str
952        :param user_keys: If set to True, users are allowed to supply document
953            keys. If set to False, the key generator is solely responsible for
954            supplying the key values.
955        :type user_keys: bool
956        :param key_increment: Key increment value. Applies only when value of
957            **key_generator** is set to "autoincrement".
958        :type key_increment: int
959        :param key_offset: Key offset value. Applies only when value of
960            **key_generator** is set to "autoincrement".
961        :type key_offset: int
962        :param shard_fields: Field(s) used to determine the target shard.
963        :type shard_fields: [str]
964        :param shard_count: Number of shards to create.
965        :type shard_count: int
966        :param replication_factor: Number of copies of each shard on different
967            servers in a cluster. Allowed values are 1 (only one copy is kept
968            and no synchronous replication), and n (n-1 replicas are kept and
969            any two copies are replicated across servers synchronously, meaning
970            every write to the master is copied to all slaves before operation
971            is reported successful).
972        :type replication_factor: int
973        :param shard_like: Name of prototype collection whose sharding
974            specifics are imitated. Prototype collections cannot be dropped
975            before imitating collections. Applies to enterprise version of
976            ArangoDB only.
977        :type shard_like: str
978        :param sync_replication: If set to True, server reports success only
979            when collection is created in all replicas. You can set this to
980            False for faster server response, and if full replication is not a
981            concern.
982        :type sync_replication: bool
983        :param enforce_replication_factor: Check if there are enough replicas
984            available at creation time, or halt the operation.
985        :type enforce_replication_factor: bool
986        :param sharding_strategy: Sharding strategy. Available for ArangoDB
987            version  and up only. Possible values are "community-compat",
988            "enterprise-compat", "enterprise-smart-edge-compat", "hash" and
989            "enterprise-hash-smart-edge". Refer to ArangoDB documentation for
990            more details on each value.
991        :type sharding_strategy: str
992        :param smart_join_attribute: Attribute of the collection which must
993            contain the shard key value of the smart join collection. The shard
994            key for the documents must contain the value of this attribute,
995            followed by a colon ":" and the primary key of the document.
996            Requires parameter **shard_like** to be set to the name of another
997            collection, and parameter **shard_fields** to be set to a single
998            shard key attribute, with another colon ":" at the end. Available
999            only for enterprise version of ArangoDB.
1000        :type smart_join_attribute: str
1001        :param write_concern: Write concern for the collection. Determines how
1002            many copies of each shard are required to be in sync on different
1003            DBServers. If there are less than these many copies in the cluster
1004            a shard will refuse to write. Writes to shards with enough
1005            up-to-date copies will succeed at the same time. The value of this
1006            parameter cannot be larger than that of **replication_factor**.
1007            Default value is 1. Used for clusters only.
1008        :type write_concern: int
1009        :param schema: Optional dict specifying the collection level schema
1010            for documents. See ArangoDB documentation for more information on
1011            document schema validation.
1012        :type schema: dict
1013        :return: Standard collection API wrapper.
1014        :rtype: arango.collection.StandardCollection
1015        :raise arango.exceptions.CollectionCreateError: If create fails.
1016        """
1017        key_options: Json = {"type": key_generator, "allowUserKeys": user_keys}
1018        if key_increment is not None:
1019            key_options["increment"] = key_increment
1020        if key_offset is not None:
1021            key_options["offset"] = key_offset
1022
1023        data: Json = {
1024            "name": name,
1025            "waitForSync": sync,
1026            "isSystem": system,
1027            "keyOptions": key_options,
1028            "type": 3 if edge else 2,
1029        }
1030        if shard_count is not None:
1031            data["numberOfShards"] = shard_count
1032        if shard_fields is not None:
1033            data["shardKeys"] = shard_fields
1034        if replication_factor is not None:
1035            data["replicationFactor"] = replication_factor
1036        if shard_like is not None:
1037            data["distributeShardsLike"] = shard_like
1038        if sharding_strategy is not None:
1039            data["shardingStrategy"] = sharding_strategy
1040        if smart_join_attribute is not None:
1041            data["smartJoinAttribute"] = smart_join_attribute
1042        if write_concern is not None:
1043            data["writeConcern"] = write_concern
1044        if schema is not None:
1045            data["schema"] = schema
1046
1047        params: Params = {}
1048        if sync_replication is not None:
1049            params["waitForSyncReplication"] = sync_replication
1050        if enforce_replication_factor is not None:
1051            params["enforceReplicationFactor"] = enforce_replication_factor
1052
1053        request = Request(
1054            method="post", endpoint="/_api/collection", params=params, data=data
1055        )
1056
1057        def response_handler(resp: Response) -> StandardCollection:
1058            if resp.is_success:
1059                return self.collection(name)
1060            raise CollectionCreateError(resp, request)
1061
1062        return self._execute(request, response_handler)
1063
1064    def delete_collection(
1065        self, name: str, ignore_missing: bool = False, system: Optional[bool] = None
1066    ) -> Result[bool]:
1067        """Delete the collection.
1068
1069        :param name: Collection name.
1070        :type name: str
1071        :param ignore_missing: Do not raise an exception on missing collection.
1072        :type ignore_missing: bool
1073        :param system: Whether the collection is a system collection.
1074        :type system: bool
1075        :return: True if collection was deleted successfully, False if
1076            collection was not found and **ignore_missing** was set to True.
1077        :rtype: bool
1078        :raise arango.exceptions.CollectionDeleteError: If delete fails.
1079        """
1080        params: Params = {}
1081        if system is not None:
1082            params["isSystem"] = system
1083
1084        request = Request(
1085            method="delete", endpoint=f"/_api/collection/{name}", params=params
1086        )
1087
1088        def response_handler(resp: Response) -> bool:
1089            if resp.error_code == 1203 and ignore_missing:
1090                return False
1091            if not resp.is_success:
1092                raise CollectionDeleteError(resp, request)
1093            return True
1094
1095        return self._execute(request, response_handler)
1096
1097    ####################
1098    # Graph Management #
1099    ####################
1100
1101    def graph(self, name: str) -> Graph:
1102        """Return the graph API wrapper.
1103
1104        :param name: Graph name.
1105        :type name: str
1106        :return: Graph API wrapper.
1107        :rtype: arango.graph.Graph
1108        """
1109        return Graph(self._conn, self._executor, name)
1110
1111    def has_graph(self, name: str) -> Result[bool]:
1112        """Check if a graph exists in the database.
1113
1114        :param name: Graph name.
1115        :type name: str
1116        :return: True if graph exists, False otherwise.
1117        :rtype: bool
1118        """
1119        request = Request(method="get", endpoint="/_api/gharial")
1120
1121        def response_handler(resp: Response) -> bool:
1122            if not resp.is_success:
1123                raise GraphListError(resp, request)
1124            return any(name == graph["_key"] for graph in resp.body["graphs"])
1125
1126        return self._execute(request, response_handler)
1127
1128    def graphs(self) -> Result[Jsons]:
1129        """List all graphs in the database.
1130
1131        :return: Graphs in the database.
1132        :rtype: [dict]
1133        :raise arango.exceptions.GraphListError: If retrieval fails.
1134        """
1135        request = Request(method="get", endpoint="/_api/gharial")
1136
1137        def response_handler(resp: Response) -> Jsons:
1138            if not resp.is_success:
1139                raise GraphListError(resp, request)
1140            return [
1141                {
1142                    "id": body["_id"],
1143                    "name": body["_key"],
1144                    "revision": body["_rev"],
1145                    "orphan_collections": body["orphanCollections"],
1146                    "edge_definitions": [
1147                        {
1148                            "edge_collection": definition["collection"],
1149                            "from_vertex_collections": definition["from"],
1150                            "to_vertex_collections": definition["to"],
1151                        }
1152                        for definition in body["edgeDefinitions"]
1153                    ],
1154                    "shard_count": body.get("numberOfShards"),
1155                    "replication_factor": body.get("replicationFactor"),
1156                }
1157                for body in resp.body["graphs"]
1158            ]
1159
1160        return self._execute(request, response_handler)
1161
1162    def create_graph(
1163        self,
1164        name: str,
1165        edge_definitions: Optional[Sequence[Json]] = None,
1166        orphan_collections: Optional[Sequence[str]] = None,
1167        smart: Optional[bool] = None,
1168        smart_field: Optional[str] = None,
1169        shard_count: Optional[int] = None,
1170    ) -> Result[Graph]:
1171        """Create a new graph.
1172
1173        :param name: Graph name.
1174        :type name: str
1175        :param edge_definitions: List of edge definitions, where each edge
1176            definition entry is a dictionary with fields "edge_collection",
1177            "from_vertex_collections" and "to_vertex_collections" (see below
1178            for example).
1179        :type edge_definitions: [dict] | None
1180        :param orphan_collections: Names of additional vertex collections that
1181            are not in edge definitions.
1182        :type orphan_collections: [str] | None
1183        :param smart: If set to True, sharding is enabled (see parameter
1184            **smart_field** below). Applies only to enterprise version of
1185            ArangoDB.
1186        :type smart: bool | None
1187        :param smart_field: Document field used to shard the vertices of the
1188            graph. To use this, parameter **smart** must be set to True and
1189            every vertex in the graph must have the smart field. Applies only
1190            to enterprise version of ArangoDB.
1191        :type smart_field: str | None
1192        :param shard_count: Number of shards used for every collection in the
1193            graph. To use this, parameter **smart** must be set to True and
1194            every vertex in the graph must have the smart field. This number
1195            cannot be modified later once set. Applies only to enterprise
1196            version of ArangoDB.
1197        :type shard_count: int | None
1198        :return: Graph API wrapper.
1199        :rtype: arango.graph.Graph
1200        :raise arango.exceptions.GraphCreateError: If create fails.
1201
1202        Here is an example entry for parameter **edge_definitions**:
1203
1204        .. code-block:: python
1205
1206            {
1207                'edge_collection': 'teach',
1208                'from_vertex_collections': ['teachers'],
1209                'to_vertex_collections': ['lectures']
1210            }
1211        """
1212        data: Json = {"name": name, "options": dict()}
1213        if edge_definitions is not None:
1214            data["edgeDefinitions"] = [
1215                {
1216                    "collection": definition["edge_collection"],
1217                    "from": definition["from_vertex_collections"],
1218                    "to": definition["to_vertex_collections"],
1219                }
1220                for definition in edge_definitions
1221            ]
1222        if orphan_collections is not None:
1223            data["orphanCollections"] = orphan_collections
1224        if smart is not None:  # pragma: no cover
1225            data["isSmart"] = smart
1226        if smart_field is not None:  # pragma: no cover
1227            data["options"]["smartGraphAttribute"] = smart_field
1228        if shard_count is not None:  # pragma: no cover
1229            data["options"]["numberOfShards"] = shard_count
1230
1231        request = Request(method="post", endpoint="/_api/gharial", data=data)
1232
1233        def response_handler(resp: Response) -> Graph:
1234            if resp.is_success:
1235                return Graph(self._conn, self._executor, name)
1236            raise GraphCreateError(resp, request)
1237
1238        return self._execute(request, response_handler)
1239
1240    def delete_graph(
1241        self,
1242        name: str,
1243        ignore_missing: bool = False,
1244        drop_collections: Optional[bool] = None,
1245    ) -> Result[bool]:
1246        """Drop the graph of the given name from the database.
1247
1248        :param name: Graph name.
1249        :type name: str
1250        :param ignore_missing: Do not raise an exception on missing graph.
1251        :type ignore_missing: bool
1252        :param drop_collections: Drop the collections of the graph also. This
1253            is only if they are not in use by other graphs.
1254        :type drop_collections: bool | None
1255        :return: True if graph was deleted successfully, False if graph was not
1256            found and **ignore_missing** was set to True.
1257        :rtype: bool
1258        :raise arango.exceptions.GraphDeleteError: If delete fails.
1259        """
1260        params: Params = {}
1261        if drop_collections is not None:
1262            params["dropCollections"] = drop_collections
1263
1264        request = Request(
1265            method="delete", endpoint=f"/_api/gharial/{name}", params=params
1266        )
1267
1268        def response_handler(resp: Response) -> bool:
1269            if resp.error_code == 1924 and ignore_missing:
1270                return False
1271            if not resp.is_success:
1272                raise GraphDeleteError(resp, request)
1273            return True
1274
1275        return self._execute(request, response_handler)
1276
1277    #######################
1278    # Document Management #
1279    #######################
1280
1281    def has_document(
1282        self, document: Json, rev: Optional[str] = None, check_rev: bool = True
1283    ) -> Result[bool]:
1284        """Check if a document exists.
1285
1286        :param document: Document ID or body with "_id" field.
1287        :type document: str | dict
1288        :param rev: Expected document revision. Overrides value of "_rev" field
1289            in **document** if present.
1290        :type rev: str | None
1291        :param check_rev: If set to True, revision of **document** (if given)
1292            is compared against the revision of target document.
1293        :type check_rev: bool
1294        :return: True if document exists, False otherwise.
1295        :rtype: bool
1296        :raise arango.exceptions.DocumentInError: If check fails.
1297        :raise arango.exceptions.DocumentRevisionError: If revisions mismatch.
1298        """
1299        return self._get_col_by_doc(document).has(
1300            document=document, rev=rev, check_rev=check_rev
1301        )
1302
1303    def document(
1304        self, document: Json, rev: Optional[str] = None, check_rev: bool = True
1305    ) -> Result[Optional[Json]]:
1306        """Return a document.
1307
1308        :param document: Document ID or body with "_id" field.
1309        :type document: str | dict
1310        :param rev: Expected document revision. Overrides the value of "_rev"
1311            field in **document** if present.
1312        :type rev: str | None
1313        :param check_rev: If set to True, revision of **document** (if given)
1314            is compared against the revision of target document.
1315        :type check_rev: bool
1316        :return: Document, or None if not found.
1317        :rtype: dict | None
1318        :raise arango.exceptions.DocumentGetError: If retrieval fails.
1319        :raise arango.exceptions.DocumentRevisionError: If revisions mismatch.
1320        """
1321        return self._get_col_by_doc(document).get(
1322            document=document, rev=rev, check_rev=check_rev
1323        )
1324
1325    def insert_document(
1326        self,
1327        collection: str,
1328        document: Json,
1329        return_new: bool = False,
1330        sync: Optional[bool] = None,
1331        silent: bool = False,
1332        overwrite: bool = False,
1333        return_old: bool = False,
1334        overwrite_mode: Optional[str] = None,
1335        keep_none: Optional[bool] = None,
1336        merge: Optional[bool] = None,
1337    ) -> Result[Union[bool, Json]]:
1338        """Insert a new document.
1339
1340        :param collection: Collection name.
1341        :type collection: str
1342        :param document: Document to insert. If it contains the "_key" or "_id"
1343            field, the value is used as the key of the new document (otherwise
1344            it is auto-generated). Any "_rev" field is ignored.
1345        :type document: dict
1346        :param return_new: Include body of the new document in the returned
1347            metadata. Ignored if parameter **silent** is set to True.
1348        :type return_new: bool
1349        :param sync: Block until operation is synchronized to disk.
1350        :type sync: bool | None
1351        :param silent: If set to True, no document metadata is returned. This
1352            can be used to save resources.
1353        :type silent: bool
1354        :param overwrite: If set to True, operation does not fail on duplicate
1355            key and the existing document is replaced.
1356        :type overwrite: bool
1357        :param return_old: Include body of the old document if replaced.
1358            Applies only when value of **overwrite** is set to True.
1359        :type return_old: bool
1360        :param overwrite_mode: Overwrite behavior used when the document key
1361            exists already. Allowed values are "replace" (replace-insert) or
1362            "update" (update-insert). Implicitly sets the value of parameter
1363            **overwrite**.
1364        :type overwrite_mode: str | None
1365        :param keep_none: If set to True, fields with value None are retained
1366            in the document. Otherwise, they are removed completely. Applies
1367            only when **overwrite_mode** is set to "update" (update-insert).
1368        :type keep_none: bool | None
1369        :param merge: If set to True (default), sub-dictionaries are merged
1370            instead of the new one overwriting the old one. Applies only when
1371            **overwrite_mode** is set to "update" (update-insert).
1372        :type merge: bool | None
1373        :return: Document metadata (e.g. document key, revision) or True if
1374            parameter **silent** was set to True.
1375        :rtype: bool | dict
1376        :raise arango.exceptions.DocumentInsertError: If insert fails.
1377        """
1378        return self.collection(collection).insert(
1379            document=document,
1380            return_new=return_new,
1381            sync=sync,
1382            silent=silent,
1383            overwrite=overwrite,
1384            return_old=return_old,
1385            overwrite_mode=overwrite_mode,
1386            keep_none=keep_none,
1387            merge=merge,
1388        )
1389
1390    def update_document(
1391        self,
1392        document: Json,
1393        check_rev: bool = True,
1394        merge: bool = True,
1395        keep_none: bool = True,
1396        return_new: bool = False,
1397        return_old: bool = False,
1398        sync: Optional[bool] = None,
1399        silent: bool = False,
1400    ) -> Result[Union[bool, Json]]:
1401        """Update a document.
1402
1403        :param document: Partial or full document with the updated values. It
1404            must contain the "_id" field.
1405        :type document: dict
1406        :param check_rev: If set to True, revision of **document** (if given)
1407            is compared against the revision of target document.
1408        :type check_rev: bool
1409        :param merge: If set to True, sub-dictionaries are merged instead of
1410            the new one overwriting the old one.
1411        :type merge: bool | None
1412        :param keep_none: If set to True, fields with value None are retained
1413            in the document. Otherwise, they are removed completely.
1414        :type keep_none: bool | None
1415        :param return_new: Include body of the new document in the result.
1416        :type return_new: bool
1417        :param return_old: Include body of the old document in the result.
1418        :type return_old: bool
1419        :param sync: Block until operation is synchronized to disk.
1420        :type sync: bool | None
1421        :param silent: If set to True, no document metadata is returned. This
1422            can be used to save resources.
1423        :type silent: bool
1424        :return: Document metadata (e.g. document key, revision) or True if
1425            parameter **silent** was set to True.
1426        :rtype: bool | dict
1427        :raise arango.exceptions.DocumentUpdateError: If update fails.
1428        :raise arango.exceptions.DocumentRevisionError: If revisions mismatch.
1429        """
1430        return self._get_col_by_doc(document).update(
1431            document=document,
1432            check_rev=check_rev,
1433            merge=merge,
1434            keep_none=keep_none,
1435            return_new=return_new,
1436            return_old=return_old,
1437            sync=sync,
1438            silent=silent,
1439        )
1440
1441    def replace_document(
1442        self,
1443        document: Json,
1444        check_rev: bool = True,
1445        return_new: bool = False,
1446        return_old: bool = False,
1447        sync: Optional[bool] = None,
1448        silent: bool = False,
1449    ) -> Result[Union[bool, Json]]:
1450        """Replace a document.
1451
1452        :param document: New document to replace the old one with. It must
1453            contain the "_id" field. Edge document must also have "_from" and
1454            "_to" fields.
1455        :type document: dict
1456        :param check_rev: If set to True, revision of **document** (if given)
1457            is compared against the revision of target document.
1458        :type check_rev: bool
1459        :param return_new: Include body of the new document in the result.
1460        :type return_new: bool
1461        :param return_old: Include body of the old document in the result.
1462        :type return_old: bool
1463        :param sync: Block until operation is synchronized to disk.
1464        :type sync: bool | None
1465        :param silent: If set to True, no document metadata is returned. This
1466            can be used to save resources.
1467        :type silent: bool
1468        :return: Document metadata (e.g. document key, revision) or True if
1469            parameter **silent** was set to True.
1470        :rtype: bool | dict
1471        :raise arango.exceptions.DocumentReplaceError: If replace fails.
1472        :raise arango.exceptions.DocumentRevisionError: If revisions mismatch.
1473        """
1474        return self._get_col_by_doc(document).replace(
1475            document=document,
1476            check_rev=check_rev,
1477            return_new=return_new,
1478            return_old=return_old,
1479            sync=sync,
1480            silent=silent,
1481        )
1482
1483    def delete_document(
1484        self,
1485        document: Union[str, Json],
1486        rev: Optional[str] = None,
1487        check_rev: bool = True,
1488        ignore_missing: bool = False,
1489        return_old: bool = False,
1490        sync: Optional[bool] = None,
1491        silent: bool = False,
1492    ) -> Result[Union[bool, Json]]:
1493        """Delete a document.
1494
1495        :param document: Document ID, key or body. Document body must contain
1496            the "_id" field.
1497        :type document: str | dict
1498        :param rev: Expected document revision. Overrides the value of "_rev"
1499            field in **document** if present.
1500        :type rev: str | None
1501        :param check_rev: If set to True, revision of **document** (if given)
1502            is compared against the revision of target document.
1503        :type check_rev: bool
1504        :param ignore_missing: Do not raise an exception on missing document.
1505            This parameter has no effect in transactions where an exception is
1506            always raised on failures.
1507        :type ignore_missing: bool
1508        :param return_old: Include body of the old document in the result.
1509        :type return_old: bool
1510        :param sync: Block until operation is synchronized to disk.
1511        :type sync: bool | None
1512        :param silent: If set to True, no document metadata is returned. This
1513            can be used to save resources.
1514        :type silent: bool
1515        :return: Document metadata (e.g. document key, revision), or True if
1516            parameter **silent** was set to True, or False if document was not
1517            found and **ignore_missing** was set to True (does not apply in
1518            transactions).
1519        :rtype: bool | dict
1520        :raise arango.exceptions.DocumentDeleteError: If delete fails.
1521        :raise arango.exceptions.DocumentRevisionError: If revisions mismatch.
1522        """
1523        return self._get_col_by_doc(document).delete(
1524            document=document,
1525            rev=rev,
1526            check_rev=check_rev,
1527            ignore_missing=ignore_missing,
1528            return_old=return_old,
1529            sync=sync,
1530            silent=silent,
1531        )
1532
1533    ###################
1534    # Task Management #
1535    ###################
1536
1537    def tasks(self) -> Result[Jsons]:
1538        """Return all currently active server tasks.
1539
1540        :return: Currently active server tasks.
1541        :rtype: [dict]
1542        :raise arango.exceptions.TaskListError: If retrieval fails.
1543        """
1544        request = Request(method="get", endpoint="/_api/tasks")
1545
1546        def response_handler(resp: Response) -> Jsons:
1547            if not resp.is_success:
1548                raise TaskListError(resp, request)
1549            result: Jsons = resp.body
1550            return result
1551
1552        return self._execute(request, response_handler)
1553
1554    def task(self, task_id: str) -> Result[Json]:
1555        """Return the details of an active server task.
1556
1557        :param task_id: Server task ID.
1558        :type task_id: str
1559        :return: Server task details.
1560        :rtype: dict
1561        :raise arango.exceptions.TaskGetError: If retrieval fails.
1562        """
1563        request = Request(method="get", endpoint=f"/_api/tasks/{task_id}")
1564
1565        def response_handler(resp: Response) -> Json:
1566            if resp.is_success:
1567                return format_body(resp.body)
1568            raise TaskGetError(resp, request)
1569
1570        return self._execute(request, response_handler)
1571
1572    def create_task(
1573        self,
1574        name: str,
1575        command: str,
1576        params: Optional[Json] = None,
1577        period: Optional[int] = None,
1578        offset: Optional[int] = None,
1579        task_id: Optional[str] = None,
1580    ) -> Result[Json]:
1581        """Create a new server task.
1582
1583        :param name: Name of the server task.
1584        :type name: str
1585        :param command: Javascript command to execute.
1586        :type command: str
1587        :param params: Optional parameters passed into the Javascript command.
1588        :type params: dict | None
1589        :param period: Number of seconds to wait between executions. If set
1590            to 0, the new task will be "timed", meaning it will execute only
1591            once and be deleted afterwards.
1592        :type period: int | None
1593        :param offset: Initial delay before execution in seconds.
1594        :type offset: int | None
1595        :param task_id: Pre-defined ID for the new server task.
1596        :type task_id: str | None
1597        :return: Details of the new task.
1598        :rtype: dict
1599        :raise arango.exceptions.TaskCreateError: If create fails.
1600        """
1601        data: Json = {"name": name, "command": command}
1602        if params is not None:
1603            data["params"] = params
1604        if task_id is not None:
1605            data["id"] = task_id
1606        if period is not None:
1607            data["period"] = period
1608        if offset is not None:
1609            data["offset"] = offset
1610
1611        if task_id is None:
1612            task_id = ""
1613
1614        request = Request(method="post", endpoint=f"/_api/tasks/{task_id}", data=data)
1615
1616        def response_handler(resp: Response) -> Json:
1617            if resp.is_success:
1618                return format_body(resp.body)
1619            raise TaskCreateError(resp, request)
1620
1621        return self._execute(request, response_handler)
1622
1623    def delete_task(self, task_id: str, ignore_missing: bool = False) -> Result[bool]:
1624        """Delete a server task.
1625
1626        :param task_id: Server task ID.
1627        :type task_id: str
1628        :param ignore_missing: Do not raise an exception on missing task.
1629        :type ignore_missing: bool
1630        :return: True if task was successfully deleted, False if task was not
1631            found and **ignore_missing** was set to True.
1632        :rtype: bool
1633        :raise arango.exceptions.TaskDeleteError: If delete fails.
1634        """
1635        request = Request(method="delete", endpoint=f"/_api/tasks/{task_id}")
1636
1637        def response_handler(resp: Response) -> bool:
1638            if resp.error_code == 1852 and ignore_missing:
1639                return False
1640            if not resp.is_success:
1641                raise TaskDeleteError(resp, request)
1642            return True
1643
1644        return self._execute(request, response_handler)
1645
1646    ###################
1647    # User Management #
1648    ###################
1649
1650    def has_user(self, username: str) -> Result[bool]:
1651        """Check if user exists.
1652
1653        :param username: Username.
1654        :type username: str
1655        :return: True if user exists, False otherwise.
1656        :rtype: bool
1657        """
1658        request = Request(method="get", endpoint="/_api/user")
1659
1660        def response_handler(resp: Response) -> bool:
1661            if not resp.is_success:
1662                raise UserListError(resp, request)
1663            return any(user["user"] == username for user in resp.body["result"])
1664
1665        return self._execute(request, response_handler)
1666
1667    def users(self) -> Result[Jsons]:
1668        """Return all user details.
1669
1670        :return: List of user details.
1671        :rtype: [dict]
1672        :raise arango.exceptions.UserListError: If retrieval fails.
1673        """
1674        request = Request(method="get", endpoint="/_api/user")
1675
1676        def response_handler(resp: Response) -> Jsons:
1677            if not resp.is_success:
1678                raise UserListError(resp, request)
1679            return [
1680                {
1681                    "username": record["user"],
1682                    "active": record["active"],
1683                    "extra": record["extra"],
1684                }
1685                for record in resp.body["result"]
1686            ]
1687
1688        return self._execute(request, response_handler)
1689
1690    def user(self, username: str) -> Result[Json]:
1691        """Return user details.
1692
1693        :param username: Username.
1694        :type username: str
1695        :return: User details.
1696        :rtype: dict
1697        :raise arango.exceptions.UserGetError: If retrieval fails.
1698        """
1699        request = Request(method="get", endpoint=f"/_api/user/{username}")
1700
1701        def response_handler(resp: Response) -> Json:
1702            if not resp.is_success:
1703                raise UserGetError(resp, request)
1704            return {
1705                "username": resp.body["user"],
1706                "active": resp.body["active"],
1707                "extra": resp.body["extra"],
1708            }
1709
1710        return self._execute(request, response_handler)
1711
1712    def create_user(
1713        self,
1714        username: str,
1715        password: Optional[str] = None,
1716        active: Optional[bool] = None,
1717        extra: Optional[Json] = None,
1718    ) -> Result[Json]:
1719        """Create a new user.
1720
1721        :param username: Username.
1722        :type username: str
1723        :param password: Password.
1724        :type password: str | None
1725        :param active: True if user is active, False otherwise.
1726        :type active: bool | None
1727        :param extra: Additional data for the user.
1728        :type extra: dict | None
1729        :return: New user details.
1730        :rtype: dict
1731        :raise arango.exceptions.UserCreateError: If create fails.
1732        """
1733        data: Json = {"user": username, "passwd": password, "active": active}
1734        if extra is not None:
1735            data["extra"] = extra
1736
1737        request = Request(method="post", endpoint="/_api/user", data=data)
1738
1739        def response_handler(resp: Response) -> Json:
1740            if not resp.is_success:
1741                raise UserCreateError(resp, request)
1742            return {
1743                "username": resp.body["user"],
1744                "active": resp.body["active"],
1745                "extra": resp.body["extra"],
1746            }
1747
1748        return self._execute(request, response_handler)
1749
1750    def update_user(
1751        self,
1752        username: str,
1753        password: Optional[str] = None,
1754        active: Optional[bool] = None,
1755        extra: Optional[Json] = None,
1756    ) -> Result[Json]:
1757        """Update a user.
1758
1759        :param username: Username.
1760        :type username: str
1761        :param password: New password.
1762        :type password: str | None
1763        :param active: Whether the user is active.
1764        :type active: bool | None
1765        :param extra: Additional data for the user.
1766        :type extra: dict | None
1767        :return: New user details.
1768        :rtype: dict
1769        :raise arango.exceptions.UserUpdateError: If update fails.
1770        """
1771        data: Json = {}
1772        if password is not None:
1773            data["passwd"] = password
1774        if active is not None:
1775            data["active"] = active
1776        if extra is not None:
1777            data["extra"] = extra
1778
1779        request = Request(
1780            method="patch",
1781            endpoint=f"/_api/user/{username}",
1782            data=data,
1783        )
1784
1785        def response_handler(resp: Response) -> Json:
1786            if not resp.is_success:
1787                raise UserUpdateError(resp, request)
1788            return {
1789                "username": resp.body["user"],
1790                "active": resp.body["active"],
1791                "extra": resp.body["extra"],
1792            }
1793
1794        return self._execute(request, response_handler)
1795
1796    def replace_user(
1797        self,
1798        username: str,
1799        password: str,
1800        active: Optional[bool] = None,
1801        extra: Optional[Json] = None,
1802    ) -> Result[Json]:
1803        """Replace a user.
1804
1805        :param username: Username.
1806        :type username: str
1807        :param password: New password.
1808        :type password: str
1809        :param active: Whether the user is active.
1810        :type active: bool | None
1811        :param extra: Additional data for the user.
1812        :type extra: dict | None
1813        :return: New user details.
1814        :rtype: dict
1815        :raise arango.exceptions.UserReplaceError: If replace fails.
1816        """
1817        data: Json = {"user": username, "passwd": password}
1818        if active is not None:
1819            data["active"] = active
1820        if extra is not None:
1821            data["extra"] = extra
1822
1823        request = Request(method="put", endpoint=f"/_api/user/{username}", data=data)
1824
1825        def response_handler(resp: Response) -> Json:
1826            if resp.is_success:
1827                return {
1828                    "username": resp.body["user"],
1829                    "active": resp.body["active"],
1830                    "extra": resp.body["extra"],
1831                }
1832            raise UserReplaceError(resp, request)
1833
1834        return self._execute(request, response_handler)
1835
1836    def delete_user(self, username: str, ignore_missing: bool = False) -> Result[bool]:
1837        """Delete a user.
1838
1839        :param username: Username.
1840        :type username: str
1841        :param ignore_missing: Do not raise an exception on missing user.
1842        :type ignore_missing: bool
1843        :return: True if user was deleted successfully, False if user was not
1844            found and **ignore_missing** was set to True.
1845        :rtype: bool
1846        :raise arango.exceptions.UserDeleteError: If delete fails.
1847        """
1848        request = Request(method="delete", endpoint=f"/_api/user/{username}")
1849
1850        def response_handler(resp: Response) -> bool:
1851            if resp.is_success:
1852                return True
1853            elif resp.status_code == 404 and ignore_missing:
1854                return False
1855            raise UserDeleteError(resp, request)
1856
1857        return self._execute(request, response_handler)
1858
1859    #########################
1860    # Permission Management #
1861    #########################
1862
1863    def permissions(self, username: str) -> Result[Json]:
1864        """Return user permissions for all databases and collections.
1865
1866        :param username: Username.
1867        :type username: str
1868        :return: User permissions for all databases and collections.
1869        :rtype: dict
1870        :raise arango.exceptions.PermissionListError: If retrieval fails.
1871        """
1872        request = Request(
1873            method="get",
1874            endpoint=f"/_api/user/{username}/database",
1875            params={"full": True},
1876        )
1877
1878        def response_handler(resp: Response) -> Json:
1879            if resp.is_success:
1880                result: Json = resp.body["result"]
1881                return result
1882            raise PermissionListError(resp, request)
1883
1884        return self._execute(request, response_handler)
1885
1886    def permission(
1887        self, username: str, database: str, collection: Optional[str] = None
1888    ) -> Result[str]:
1889        """Return user permission for a specific database or collection.
1890
1891        :param username: Username.
1892        :type username: str
1893        :param database: Database name.
1894        :type database: str
1895        :param collection: Collection name.
1896        :type collection: str | None
1897        :return: Permission for given database or collection.
1898        :rtype: str
1899        :raise arango.exceptions.PermissionGetError: If retrieval fails.
1900        """
1901        endpoint = f"/_api/user/{username}/database/{database}"
1902        if collection is not None:
1903            endpoint += "/" + collection
1904        request = Request(method="get", endpoint=endpoint)
1905
1906        def response_handler(resp: Response) -> str:
1907            if resp.is_success:
1908                return str(resp.body["result"])
1909            raise PermissionGetError(resp, request)
1910
1911        return self._execute(request, response_handler)
1912
1913    def update_permission(
1914        self,
1915        username: str,
1916        permission: str,
1917        database: str,
1918        collection: Optional[str] = None,
1919    ) -> Result[bool]:
1920        """Update user permission for a specific database or collection.
1921
1922        :param username: Username.
1923        :type username: str
1924        :param permission: Allowed values are "rw" (read and write), "ro"
1925            (read only) or "none" (no access).
1926        :type permission: str
1927        :param database: Database name.
1928        :type database: str
1929        :param collection: Collection name.
1930        :type collection: str | None
1931        :return: True if access was granted successfully.
1932        :rtype: bool
1933        :raise arango.exceptions.PermissionUpdateError: If update fails.
1934        """
1935        endpoint = f"/_api/user/{username}/database/{database}"
1936        if collection is not None:
1937            endpoint += "/" + collection
1938
1939        request = Request(method="put", endpoint=endpoint, data={"grant": permission})
1940
1941        def response_handler(resp: Response) -> bool:
1942            if resp.is_success:
1943                return True
1944            raise PermissionUpdateError(resp, request)
1945
1946        return self._execute(request, response_handler)
1947
1948    def reset_permission(
1949        self, username: str, database: str, collection: Optional[str] = None
1950    ) -> Result[bool]:
1951        """Reset user permission for a specific database or collection.
1952
1953        :param username: Username.
1954        :type username: str
1955        :param database: Database name.
1956        :type database: str
1957        :param collection: Collection name.
1958        :type collection: str
1959        :return: True if permission was reset successfully.
1960        :rtype: bool
1961        :raise arango.exceptions.PermissionRestError: If reset fails.
1962        """
1963        endpoint = f"/_api/user/{username}/database/{database}"
1964        if collection is not None:
1965            endpoint += "/" + collection
1966
1967        request = Request(method="delete", endpoint=endpoint)
1968
1969        def response_handler(resp: Response) -> bool:
1970            if resp.is_success:
1971                return True
1972            raise PermissionResetError(resp, request)
1973
1974        return self._execute(request, response_handler)
1975
1976    ########################
1977    # Async Job Management #
1978    ########################
1979
1980    def async_jobs(self, status: str, count: Optional[int] = None) -> Result[List[str]]:
1981        """Return IDs of async jobs with given status.
1982
1983        :param status: Job status (e.g. "pending", "done").
1984        :type status: str
1985        :param count: Max number of job IDs to return.
1986        :type count: int
1987        :return: List of job IDs.
1988        :rtype: [str]
1989        :raise arango.exceptions.AsyncJobListError: If retrieval fails.
1990        """
1991        params: Params = {}
1992        if count is not None:
1993            params["count"] = count
1994
1995        request = Request(method="get", endpoint=f"/_api/job/{status}", params=params)
1996
1997        def response_handler(resp: Response) -> List[str]:
1998            if resp.is_success:
1999                result: List[str] = resp.body
2000                return result
2001            raise AsyncJobListError(resp, request)
2002
2003        return self._execute(request, response_handler)
2004
2005    def clear_async_jobs(self, threshold: Optional[int] = None) -> Result[bool]:
2006        """Clear async job results from the server.
2007
2008        Async jobs that are still queued or running are not stopped.
2009
2010        :param threshold: If specified, only the job results created prior to
2011            the threshold (a unix timestamp) are deleted. Otherwise, all job
2012            results are deleted.
2013        :type threshold: int | None
2014        :return: True if job results were cleared successfully.
2015        :rtype: bool
2016        :raise arango.exceptions.AsyncJobClearError: If operation fails.
2017        """
2018        if threshold is None:
2019            request = Request(method="delete", endpoint="/_api/job/all")
2020        else:
2021            request = Request(
2022                method="delete",
2023                endpoint="/_api/job/expired",
2024                params={"stamp": threshold},
2025            )
2026
2027        def response_handler(resp: Response) -> bool:
2028            if resp.is_success:
2029                return True
2030            raise AsyncJobClearError(resp, request)
2031
2032        return self._execute(request, response_handler)
2033
2034    ###################
2035    # View Management #
2036    ###################
2037
2038    def views(self) -> Result[Jsons]:
2039        """Return list of views and their summaries.
2040
2041        :return: List of views.
2042        :rtype: [dict]
2043        :raise arango.exceptions.ViewListError: If retrieval fails.
2044        """
2045        request = Request(method="get", endpoint="/_api/view")
2046
2047        def response_handler(resp: Response) -> Jsons:
2048            if resp.is_success:
2049                return [format_view(view) for view in resp.body["result"]]
2050            raise ViewListError(resp, request)
2051
2052        return self._execute(request, response_handler)
2053
2054    def view(self, name: str) -> Result[Json]:
2055        """Return view details.
2056
2057        :return: View details.
2058        :rtype: dict
2059        :raise arango.exceptions.ViewGetError: If retrieval fails.
2060        """
2061        request = Request(method="get", endpoint=f"/_api/view/{name}/properties")
2062
2063        def response_handler(resp: Response) -> Json:
2064            if resp.is_success:
2065                return format_view(resp.body)
2066            raise ViewGetError(resp, request)
2067
2068        return self._execute(request, response_handler)
2069
2070    def create_view(
2071        self, name: str, view_type: str, properties: Optional[Json] = None
2072    ) -> Result[Json]:
2073        """Create a view.
2074
2075        :param name: View name.
2076        :type name: str
2077        :param view_type: View type (e.g. "arangosearch").
2078        :type view_type: str
2079        :param properties: View properties. For more information see
2080            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2081        :type properties: dict
2082        :return: View details.
2083        :rtype: dict
2084        :raise arango.exceptions.ViewCreateError: If create fails.
2085        """
2086        data: Json = {"name": name, "type": view_type}
2087
2088        if properties is not None:
2089            data.update(properties)
2090
2091        request = Request(method="post", endpoint="/_api/view", data=data)
2092
2093        def response_handler(resp: Response) -> Json:
2094            if resp.is_success:
2095                return format_view(resp.body)
2096            raise ViewCreateError(resp, request)
2097
2098        return self._execute(request, response_handler)
2099
2100    def update_view(self, name: str, properties: Json) -> Result[Json]:
2101        """Update a view.
2102
2103        :param name: View name.
2104        :type name: str
2105        :param properties: View properties. For more information see
2106            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2107        :type properties: dict
2108        :return: View details.
2109        :rtype: dict
2110        :raise arango.exceptions.ViewUpdateError: If update fails.
2111        """
2112        request = Request(
2113            method="patch",
2114            endpoint=f"/_api/view/{name}/properties",
2115            data=properties,
2116        )
2117
2118        def response_handler(resp: Response) -> Json:
2119            if resp.is_success:
2120                return format_view(resp.body)
2121            raise ViewUpdateError(resp, request)
2122
2123        return self._execute(request, response_handler)
2124
2125    def replace_view(self, name: str, properties: Json) -> Result[Json]:
2126        """Replace a view.
2127
2128        :param name: View name.
2129        :type name: str
2130        :param properties: View properties. For more information see
2131            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2132        :type properties: dict
2133        :return: View details.
2134        :rtype: dict
2135        :raise arango.exceptions.ViewReplaceError: If replace fails.
2136        """
2137        request = Request(
2138            method="put",
2139            endpoint=f"/_api/view/{name}/properties",
2140            data=properties,
2141        )
2142
2143        def response_handler(resp: Response) -> Json:
2144            if resp.is_success:
2145                return format_view(resp.body)
2146            raise ViewReplaceError(resp, request)
2147
2148        return self._execute(request, response_handler)
2149
2150    def delete_view(self, name: str, ignore_missing: bool = False) -> Result[bool]:
2151        """Delete a view.
2152
2153        :param name: View name.
2154        :type name: str
2155        :param ignore_missing: Do not raise an exception on missing view.
2156        :type ignore_missing: bool
2157        :return: True if view was deleted successfully, False if view was not
2158            found and **ignore_missing** was set to True.
2159        :rtype: bool
2160        :raise arango.exceptions.ViewDeleteError: If delete fails.
2161        """
2162        request = Request(method="delete", endpoint=f"/_api/view/{name}")
2163
2164        def response_handler(resp: Response) -> bool:
2165            if resp.error_code == 1203 and ignore_missing:
2166                return False
2167            if resp.is_success:
2168                return True
2169            raise ViewDeleteError(resp, request)
2170
2171        return self._execute(request, response_handler)
2172
2173    def rename_view(self, name: str, new_name: str) -> Result[bool]:
2174        """Rename a view.
2175
2176        :param name: View name.
2177        :type name: str
2178        :param new_name: New view name.
2179        :type new_name: str
2180        :return: True if view was renamed successfully.
2181        :rtype: bool
2182        :raise arango.exceptions.ViewRenameError: If delete fails.
2183        """
2184        request = Request(
2185            method="put",
2186            endpoint=f"/_api/view/{name}/rename",
2187            data={"name": new_name},
2188        )
2189
2190        def response_handler(resp: Response) -> bool:
2191            if resp.is_success:
2192                return True
2193            raise ViewRenameError(resp, request)
2194
2195        return self._execute(request, response_handler)
2196
2197    ################################
2198    # ArangoSearch View Management #
2199    ################################
2200
2201    def create_arangosearch_view(
2202        self, name: str, properties: Optional[Json] = None
2203    ) -> Result[Json]:
2204        """Create an ArangoSearch view.
2205
2206        :param name: View name.
2207        :type name: str
2208        :param properties: View properties. For more information see
2209            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2210        :type properties: dict | None
2211        :return: View details.
2212        :rtype: dict
2213        :raise arango.exceptions.ViewCreateError: If create fails.
2214        """
2215        data: Json = {"name": name, "type": "arangosearch"}
2216
2217        if properties is not None:
2218            data.update(properties)
2219
2220        request = Request(method="post", endpoint="/_api/view#ArangoSearch", data=data)
2221
2222        def response_handler(resp: Response) -> Json:
2223            if resp.is_success:
2224                return format_view(resp.body)
2225            raise ViewCreateError(resp, request)
2226
2227        return self._execute(request, response_handler)
2228
2229    def update_arangosearch_view(self, name: str, properties: Json) -> Result[Json]:
2230        """Update an ArangoSearch view.
2231
2232        :param name: View name.
2233        :type name: str
2234        :param properties: View properties. For more information see
2235            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2236        :type properties: dict
2237        :return: View details.
2238        :rtype: dict
2239        :raise arango.exceptions.ViewUpdateError: If update fails.
2240        """
2241        request = Request(
2242            method="patch",
2243            endpoint=f"/_api/view/{name}/properties#ArangoSearch",
2244            data=properties,
2245        )
2246
2247        def response_handler(resp: Response) -> Json:
2248            if resp.is_success:
2249                return format_view(resp.body)
2250            raise ViewUpdateError(resp, request)
2251
2252        return self._execute(request, response_handler)
2253
2254    def replace_arangosearch_view(self, name: str, properties: Json) -> Result[Json]:
2255        """Replace an ArangoSearch view.
2256
2257        :param name: View name.
2258        :type name: str
2259        :param properties: View properties. For more information see
2260            https://www.arangodb.com/docs/stable/http/views-arangosearch.html
2261        :type properties: dict
2262        :return: View details.
2263        :rtype: dict
2264        :raise arango.exceptions.ViewReplaceError: If replace fails.
2265        """
2266        request = Request(
2267            method="put",
2268            endpoint=f"/_api/view/{name}/properties#ArangoSearch",
2269            data=properties,
2270        )
2271
2272        def response_handler(resp: Response) -> Json:
2273            if resp.is_success:
2274                return format_view(resp.body)
2275            raise ViewReplaceError(resp, request)
2276
2277        return self._execute(request, response_handler)
2278
2279    #######################
2280    # Analyzer Management #
2281    #######################
2282
2283    def analyzers(self) -> Result[Jsons]:
2284        """Return list of analyzers.
2285
2286        :return: List of analyzers.
2287        :rtype: [dict]
2288        :raise arango.exceptions.AnalyzerListError: If retrieval fails.
2289        """
2290        request = Request(method="get", endpoint="/_api/analyzer")
2291
2292        def response_handler(resp: Response) -> Jsons:
2293            if resp.is_success:
2294                result: Jsons = resp.body["result"]
2295                return result
2296            raise AnalyzerListError(resp, request)
2297
2298        return self._execute(request, response_handler)
2299
2300    def analyzer(self, name: str) -> Result[Json]:
2301        """Return analyzer details.
2302
2303        :param name: Analyzer name.
2304        :type name: str
2305        :return: Analyzer details.
2306        :rtype: dict
2307        :raise arango.exceptions.AnalyzerGetError: If retrieval fails.
2308        """
2309        request = Request(method="get", endpoint=f"/_api/analyzer/{name}")
2310
2311        def response_handler(resp: Response) -> Json:
2312            if resp.is_success:
2313                return format_body(resp.body)
2314            raise AnalyzerGetError(resp, request)
2315
2316        return self._execute(request, response_handler)
2317
2318    def create_analyzer(
2319        self,
2320        name: str,
2321        analyzer_type: str,
2322        properties: Optional[Json] = None,
2323        features: Optional[Sequence[str]] = None,
2324    ) -> Result[Json]:
2325        """Create an analyzer.
2326
2327        :param name: Analyzer name.
2328        :type name: str
2329        :param analyzer_type: Analyzer type.
2330        :type analyzer_type: str
2331        :param properties: Analyzer properties.
2332        :type properties: dict | None
2333        :param features: Analyzer features.
2334        :type features: list | None
2335        :return: Analyzer details.
2336        :rtype: dict
2337        :raise arango.exceptions.AnalyzerCreateError: If create fails.
2338        """
2339        data: Json = {"name": name, "type": analyzer_type}
2340
2341        if properties is not None:
2342            data["properties"] = properties
2343
2344        if features is not None:
2345            data["features"] = features
2346
2347        request = Request(method="post", endpoint="/_api/analyzer", data=data)
2348
2349        def response_handler(resp: Response) -> Json:
2350            if resp.is_success:
2351                result: Json = resp.body
2352                return result
2353            raise AnalyzerCreateError(resp, request)
2354
2355        return self._execute(request, response_handler)
2356
2357    def delete_analyzer(
2358        self, name: str, force: bool = False, ignore_missing: bool = False
2359    ) -> Result[bool]:
2360        """Delete an analyzer.
2361
2362        :param name: Analyzer name.
2363        :type name: str
2364        :param force: Remove the analyzer configuration even if in use.
2365        :type force: bool
2366        :param ignore_missing: Do not raise an exception on missing analyzer.
2367        :type ignore_missing: bool
2368        :return: True if analyzer was deleted successfully, False if analyzer
2369            was not found and **ignore_missing** was set to True.
2370        :rtype: bool
2371        :raise arango.exceptions.AnalyzerDeleteError: If delete fails.
2372        """
2373        request = Request(
2374            method="delete",
2375            endpoint=f"/_api/analyzer/{name}",
2376            params={"force": force},
2377        )
2378
2379        def response_handler(resp: Response) -> bool:
2380            if resp.error_code in {1202, 404} and ignore_missing:
2381                return False
2382            if resp.is_success:
2383                return True
2384            raise AnalyzerDeleteError(resp, request)
2385
2386        return self._execute(request, response_handler)
2387
2388
2389class StandardDatabase(Database):
2390    """Standard database API wrapper."""
2391
2392    def __init__(self, connection: Connection) -> None:
2393        super().__init__(connection=connection, executor=DefaultApiExecutor(connection))
2394
2395    def __repr__(self) -> str:
2396        return f"<StandardDatabase {self.name}>"
2397
2398    def begin_async_execution(self, return_result: bool = True) -> "AsyncDatabase":
2399        """Begin async execution.
2400
2401        :param return_result: If set to True, API executions return instances
2402            of :class:`arango.job.AsyncJob`, which you can use to retrieve
2403            results from server once available. If set to False, API executions
2404            return None and no results are stored on server.
2405        :type return_result: bool
2406        :return: Database API wrapper object specifically for async execution.
2407        :rtype: arango.database.AsyncDatabase
2408        """
2409        return AsyncDatabase(self._conn, return_result)
2410
2411    def begin_batch_execution(self, return_result: bool = True) -> "BatchDatabase":
2412        """Begin batch execution.
2413
2414        :param return_result: If set to True, API executions return instances
2415            of :class:`arango.job.BatchJob` that are populated with results on
2416            commit. If set to False, API executions return None and no results
2417            are tracked client-side.
2418        :type return_result: bool
2419        :return: Database API wrapper object specifically for batch execution.
2420        :rtype: arango.database.BatchDatabase
2421        """
2422        return BatchDatabase(self._conn, return_result)
2423
2424    def begin_transaction(
2425        self,
2426        read: Union[str, Sequence[str], None] = None,
2427        write: Union[str, Sequence[str], None] = None,
2428        exclusive: Union[str, Sequence[str], None] = None,
2429        sync: Optional[bool] = None,
2430        allow_implicit: Optional[bool] = None,
2431        lock_timeout: Optional[int] = None,
2432        max_size: Optional[int] = None,
2433    ) -> "TransactionDatabase":
2434        """Begin a transaction.
2435
2436        :param read: Name(s) of collections read during transaction. Read-only
2437            collections are added lazily but should be declared if possible to
2438            avoid deadlocks.
2439        :type read: str | [str] | None
2440        :param write: Name(s) of collections written to during transaction with
2441            shared access.
2442        :type write: str | [str] | None
2443        :param exclusive: Name(s) of collections written to during transaction
2444            with exclusive access.
2445        :type exclusive: str | [str] | None
2446        :param sync: Block until operation is synchronized to disk.
2447        :type sync: bool | None
2448        :param allow_implicit: Allow reading from undeclared collections.
2449        :type allow_implicit: bool | None
2450        :param lock_timeout: Timeout for waiting on collection locks. If not
2451            given, a default value is used. Setting it to 0 disables the
2452            timeout.
2453        :type lock_timeout: int | None
2454        :param max_size: Max transaction size in bytes.
2455        :type max_size: int | None
2456        :return: Database API wrapper object specifically for transactions.
2457        :rtype: arango.database.TransactionDatabase
2458        """
2459        return TransactionDatabase(
2460            connection=self._conn,
2461            read=read,
2462            write=write,
2463            exclusive=exclusive,
2464            sync=sync,
2465            allow_implicit=allow_implicit,
2466            lock_timeout=lock_timeout,
2467            max_size=max_size,
2468        )
2469
2470
2471class AsyncDatabase(Database):
2472    """Database API wrapper tailored specifically for async execution.
2473
2474    See :func:`arango.database.StandardDatabase.begin_async_execution`.
2475
2476    :param connection: HTTP connection.
2477    :param return_result: If set to True, API executions return instances of
2478        :class:`arango.job.AsyncJob`, which you can use to retrieve results
2479        from server once available. If set to False, API executions return None
2480        and no results are stored on server.
2481    :type return_result: bool
2482    """
2483
2484    def __init__(self, connection: Connection, return_result: bool) -> None:
2485        self._executor: AsyncApiExecutor
2486        super().__init__(
2487            connection=connection, executor=AsyncApiExecutor(connection, return_result)
2488        )
2489
2490    def __repr__(self) -> str:
2491        return f"<AsyncDatabase {self.name}>"
2492
2493
2494class BatchDatabase(Database):
2495    """Database API wrapper tailored specifically for batch execution.
2496
2497    See :func:`arango.database.StandardDatabase.begin_batch_execution`.
2498
2499    :param connection: HTTP connection.
2500    :param return_result: If set to True, API executions return instances of
2501        :class:`arango.job.BatchJob` that are populated with results on commit.
2502        If set to False, API executions return None and no results are tracked
2503        client-side.
2504    :type return_result: bool
2505    """
2506
2507    def __init__(self, connection: Connection, return_result: bool) -> None:
2508        self._executor: BatchApiExecutor
2509        super().__init__(
2510            connection=connection, executor=BatchApiExecutor(connection, return_result)
2511        )
2512
2513    def __repr__(self) -> str:
2514        return f"<BatchDatabase {self.name}>"
2515
2516    def __enter__(self) -> "BatchDatabase":
2517        return self
2518
2519    def __exit__(self, exception: Exception, *_: Any) -> None:
2520        if exception is None:
2521            self._executor.commit()
2522
2523    def queued_jobs(self) -> Optional[Sequence[BatchJob[Any]]]:
2524        """Return the queued batch jobs.
2525
2526        :return: Queued batch jobs or None if **return_result** parameter was
2527            set to False during initialization.
2528        :rtype: [arango.job.BatchJob] | None
2529        """
2530        return self._executor.jobs
2531
2532    def commit(self) -> Optional[Sequence[BatchJob[Any]]]:
2533        """Execute the queued requests in a single batch API request.
2534
2535        If **return_result** parameter was set to True during initialization,
2536        :class:`arango.job.BatchJob` instances are populated with results.
2537
2538        :return: Batch jobs, or None if **return_result** parameter was set to
2539            False during initialization.
2540        :rtype: [arango.job.BatchJob] | None
2541        :raise arango.exceptions.BatchStateError: If batch state is invalid
2542            (e.g. batch was already committed or the response size did not
2543            match expected).
2544        :raise arango.exceptions.BatchExecuteError: If commit fails.
2545        """
2546        return self._executor.commit()
2547
2548
2549class TransactionDatabase(Database):
2550    """Database API wrapper tailored specifically for transactions.
2551
2552    See :func:`arango.database.StandardDatabase.begin_transaction`.
2553
2554    :param connection: HTTP connection.
2555    :param read: Name(s) of collections read during transaction. Read-only
2556        collections are added lazily but should be declared if possible to
2557        avoid deadlocks.
2558    :type read: str | [str] | None
2559    :param write: Name(s) of collections written to during transaction with
2560        shared access.
2561    :type write: str | [str] | None
2562    :param exclusive: Name(s) of collections written to during transaction
2563        with exclusive access.
2564    :type exclusive: str | [str] | None
2565    :param sync: Block until operation is synchronized to disk.
2566    :type sync: bool | None
2567    :param allow_implicit: Allow reading from undeclared collections.
2568    :type allow_implicit: bool | None
2569    :param lock_timeout: Timeout for waiting on collection locks. If not given,
2570        a default value is used. Setting it to 0 disables the timeout.
2571    :type lock_timeout: int | None
2572    :param max_size: Max transaction size in bytes.
2573    :type max_size: int | None
2574    """
2575
2576    def __init__(
2577        self,
2578        connection: Connection,
2579        read: Union[str, Sequence[str], None] = None,
2580        write: Union[str, Sequence[str], None] = None,
2581        exclusive: Union[str, Sequence[str], None] = None,
2582        sync: Optional[bool] = None,
2583        allow_implicit: Optional[bool] = None,
2584        lock_timeout: Optional[int] = None,
2585        max_size: Optional[int] = None,
2586    ) -> None:
2587        self._executor: TransactionApiExecutor
2588        super().__init__(
2589            connection=connection,
2590            executor=TransactionApiExecutor(
2591                connection=connection,
2592                read=read,
2593                write=write,
2594                exclusive=exclusive,
2595                sync=sync,
2596                allow_implicit=allow_implicit,
2597                lock_timeout=lock_timeout,
2598                max_size=max_size,
2599            ),
2600        )
2601
2602    def __repr__(self) -> str:
2603        return f"<TransactionDatabase {self.name}>"
2604
2605    @property
2606    def transaction_id(self) -> str:
2607        """Return the transaction ID.
2608
2609        :return: Transaction ID.
2610        :rtype: str
2611        """
2612        return self._executor.id
2613
2614    def transaction_status(self) -> str:
2615        """Return the transaction status.
2616
2617        :return: Transaction status.
2618        :rtype: str
2619        :raise arango.exceptions.TransactionStatusError: If retrieval fails.
2620        """
2621        return self._executor.status()
2622
2623    def commit_transaction(self) -> bool:
2624        """Commit the transaction.
2625
2626        :return: True if commit was successful.
2627        :rtype: bool
2628        :raise arango.exceptions.TransactionCommitError: If commit fails.
2629        """
2630        return self._executor.commit()
2631
2632    def abort_transaction(self) -> bool:
2633        """Abort the transaction.
2634
2635        :return: True if the abort operation was successful.
2636        :rtype: bool
2637        :raise arango.exceptions.TransactionAbortError: If abort fails.
2638        """
2639        return self._executor.abort()
2640