Packages

An implementation of DBConnection for ArangoDB. Velocy and JSON over HTTP/2, the new AQL plan cache, resource APIs covering ArangoDB's HTTP surface, and pools, transactions and cursors via DBConnection. VelocyStream and Active Failover remain for 3.11.

Current section

Files

Jump to
arangox README.md
Raw

README.md

# Arangox
[![Build Status](https://travis-ci.com/suazithustra/arangox.svg?branch=master)](https://travis-ci.org/suazithustra/arangox)
An implementation of [`db_connection`](https://hex.pm/packages/db_connection)
for _ArangoDB_, which is silly because _Arangodb_ is not a transactional database (i.e.
no prepare, commit, rollback, etc.), but whatever, it's a solid connection pooler.
Arangox supports [active failover](https://www.arangodb.com/docs/stable/architecture-deployment-modes-active-failover-architecture.html).
### Peer Dependencies
Arangox requires a json library and http client to work, the defaults are `:jason` and
`:gun`:
```elixir
def deps do
[
...
{:arangox, "~> 0.1.0"},
{:jason, "~> 1.1"},
{:gun, "~> 1.3"}
]
end
```
You _might_ need to add `:gun` as an extra application in `mix.exs`:
```elixir
def application() do
[
extra_applications: [:logger, :gun])
]
end
```
To use a different json library, set the `:json_library` config to the module of your
choice:
```elixir
config :arangox, :json_library, Poison
```
Arangox already has a `Mint` client. To use it, add `:mint` to your deps instead of
`:gun` and set the `:client` start option to `Arangox.Client.Mint`:
```elixir
Arangox.start_link(client: Arangox.Client.Mint)
```
To use something else, you'd have to implement the `Arangox.Client` behaviour in a
module somewhere and set that instead. The `Arangox.Endpoint` module has utilities
for parsing _ArangoDB_ endpoints.
### Examples
```elixir
iex> {:ok, conn} = Arangox.start_link(pool_size: 10)
iex> Arangox.request(conn, :options, "/")
{:ok,
%Arangox.Request{
body: "",
headers: [{"authorization", "..."}],
method: :options,
path: "/"
},
%Arangox.Response{
body: nil,
headers: [
{"x-content-type-options", "nosniff"},
{"allow", "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"},
{"server", "ArangoDB"},
{"connection", "Keep-Alive"},
{"content-type", "text/plain; charset=utf-8"},
{"content-length", "0"}
],
status: 200
}}
iex> Arangox.options!(conn)
%Arangox.Response{
body: nil,
headers: [
{"x-content-type-options", "nosniff"},
{"allow", "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT"},
{"server", "ArangoDB"},
{"connection", "Keep-Alive"},
{"content-type", "text/plain; charset=utf-8"},
{"content-length", "0"}
],
status: 200
}
```
## Options
Arangox assumes defaults for the `:endpoints`, `:username` and `:password` options,
and [`db_connection`](https://hex.pm/packages/db_connection) assumes a default
`:pool_size` of `1` so the following:
```elixir
Arangox.start_link()
```
Is equivalent to:
```elixir
options = [
pool_size: 1,
endpoints: ["http://localhost:8529"],
username: "root",
password: ""
]
Arangox.start_link(options)
```
### Endpoints
As is common amongst _ArangoDB_ drivers, arangox takes a list of endpoints as binaries:
```elixir
endpoints = [
"http://localhost:8529",
"http://localhost:8530",
"http://localhost:8531"
]
Arangox.start_link(endpoints: endpoints)
```
Arangox will try to establish a connection with the first endpoint it can and
check it's availability (via the _ArangoDB_ api). If an endpoint is in maintenance mode
or is a follower in an _active failover_ setup, it will be skipped.
With the `read_only?` option set to `true`, arangox will try to find a server in
_readonly_ mode instead and add the _x-arango-allow-dirty-read_ header to every request:
```elixir
iex> endpoints = ["http://localhost:8003", "http://localhost:8004", "http://localhost:8005"]
iex> {:ok, conn} = Arangox.start_link(endpoints: endpoints, read_only?: true)
iex> %Arangox.Response{body: body} = Arangox.get!(conn, "/_admin/server/mode")
iex> body["mode"]
"readonly"
iex> {:error, exception} = Arangox.post(conn, "/_api/database", %{name: "newDatabase"})
iex> exception.message
"forbidden"
```
See the
[arangosh](https://www.arangodb.com/docs/stable/programs-arangosh-examples.html) or
[arangojs](https://www.arangodb.com/docs/stable/drivers/js-reference-database.html)
documentation for examples of supported endpoint formats.
### Authentication
Arangox will generate an authorization header with the `:username` and `:password`
options and add it to every request. To prevent this behavior, set the `:auth?`
option to `false`.
```elixir
iex> {:ok, conn} = Arangox.start_link(auth?: false)
iex> {:error, exception} = Arangox.get(conn, "/_admin/server/mode")
iex> exception.message
"not authorized to execute this request"
```
The header value is obfuscated in the transfomed requests returned by arangox, for
obvious reasons:
```elixir
iex> {:ok, conn} = Arangox.start_link()
iex> {:ok, request, _response} = Arangox.options(conn)
iex> request.headers
[{"authorization", "..."}]
```
### Databases
If a value is given to the `:database` option, arangox will prepend `/_db/:value`
to the path of every request that isn't already prepended. If a value is not given,
nothing is prepended (_ArangoDB_ will assume the `_system` database).
```elixir
iex> {:ok, conn} = Arangox.start_link()
iex> {:ok, request, _response} = Arangox.get(conn, "/_admin/time")
iex> request.path
"/_admin/time"
iex> {:ok, conn} = Arangox.start_link(database: "myDatabase")
iex> {:ok, request, _response} = Arangox.get(conn, "/_admin/time")
iex> request.path
"/_db/myDatabase/_admin/time"
iex> {:ok, request, _response} = Arangox.get(conn, "/_db/anotherDatabase/_admin/time")
iex> request.path
"/_db/anotherDatabase/_admin/time"
```
### Headers
Headers are given as lists of two-element tuples:
```elixir
[{"header", "value"}, {"another-header", "another-value"}]
```
When given to the `:headers` start option, they are merged with every request.
```elixir
iex> {:ok, conn} = Arangox.start_link(headers: [{"header", "value"}])
iex> {:ok, request, _response} = Arangox.options(conn)
iex> request.headers
[{"authorization", "..."}, {"header", "value"}]
```
Headers can also be passed as an argument to any request:
```elixir
iex> {:ok, conn} = Arangox.start_link()
iex> {:ok, request, _response} = Arangox.get(conn, "/_admin/time", [{"header", "value"}])
iex> request.headers
[{"header", "value"}, {"authorization", "..."}]
```
### Transport
Transport options can be specified via `:tcp_opts` and `:ssl_opts`, for non-encrypted and
encrypted connections respectively. These options are passed directly to the `:transport_opts`
option of `:gun` or `Mint`. Some transport options are set by arangox and cannot be
overridden with these options (i.e. `Mint`'s `:mode` option).
See [`:gen_tcp.connect_option()`](http://erlang.org/doc/man/gen_tcp.html#type-connect_option)
for more information on `:tcp_opts`, or [`:ssl.tls_client_option()`](http://erlang.org/doc/man/ssl.html#type-tls_client_option) for `:ssl_opts`.
The `:client_opts` option can be used to pass client-specific options to `:gun` or `Mint`.
These options are merged with and may override any of the values set by arangox. If
`:transport_opts` is set here it will override everything given to `:tcp_opts` or `:ssl_opts`,
regardless of whether or not a connection is encrypted.
See the `gun:opts()` type in the [gun docs](https://ninenines.eu/docs/en/gun/1.3/manual/gun/)
or [`connect/4`](https://hexdocs.pm/mint/Mint.HTTP.html#connect/4) in the mint docs for more
information.
## Contributing
```
mix do format, credo
docker-compose up -d
mix test
```
## Roadmap
- A VelocyStream client
- An Ecto adapter
- More descriptive logs