├── config ├── test.exs ├── config.exs └── .credo.exs ├── test ├── arangoex │ ├── aql_test.exs │ ├── bulk_test.exs │ ├── job_test.exs │ ├── wal_test.exs │ ├── admin_test.exs │ ├── index_test.exs │ ├── agency_test.exs │ ├── cursor_test.exs │ ├── cluster_test.exs │ ├── document_test.exs │ ├── graph │ │ ├── edges_test.exs │ │ └── traversal_test.exs │ ├── replication_test.exs │ ├── transaction_test.exs │ ├── connection_test.exs │ ├── response_test.exs │ ├── database_test.exs │ ├── user_test.exs │ ├── simple_test.exs │ ├── collection_test.exs │ └── graph_test.exs ├── arangoex_test.exs └── test_helper.exs ├── lib ├── arangoex │ ├── cursor.ex │ ├── bulk.ex │ ├── agency.ex │ ├── wal.ex │ ├── job.ex │ ├── cluster.ex │ ├── transaction.ex │ ├── graph │ │ ├── traversal.ex │ │ └── edges.ex │ ├── aql.ex │ ├── index.ex │ ├── response.ex │ ├── replication.ex │ ├── database.ex │ ├── document.ex │ ├── connection.ex │ ├── admin.ex │ ├── user.ex │ ├── collection.ex │ ├── simple.ex │ └── graph.ex └── arangoex.ex ├── .travis.yml ├── .github └── dependabot.yml ├── .gitignore ├── README.md ├── LICENSE.md ├── mix.exs └── mix.lock /config/test.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :arangoex, 4 | database: "test" 5 | -------------------------------------------------------------------------------- /test/arangoex/aql_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.AQLTest do 2 | # alias Arangoex.AQL 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - AQL tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/bulk_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.BulkTest do 2 | # alias Arangoex.Bulk 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Bulk tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/job_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.JobTest do 2 | # alias Arangoex.Job 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Job tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/wal_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.WALTest do 2 | # alias Arangoex.WAL 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - WAL tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/admin_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.AdminTest do 2 | # alias Arangoex.Admin 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Admin tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/index_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.IndexTest do 2 | # alias Arangoex.Index 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Index tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/agency_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.AgencyTest do 2 | # alias Arangoex.Agency 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Agency tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/cursor_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.CursorTest do 2 | # alias Arangoex.Cursor 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Cursor tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/cluster_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.ClusterTest do 2 | # alias Arangoex.Cluster 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Cluster tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/document_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.DocumentTest do 2 | # alias Arangoex.Document 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Document tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/graph/edges_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Graph.EdgesTest do 2 | # alias Arangoex.Graph.Edges 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Edges tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/replication_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.ReplicationTest do 2 | # alias Arangoex.Replication 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Replication tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/transaction_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.TransactionTest do 2 | # alias Arangoex.Transaction 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Transaction tests 7 | end 8 | -------------------------------------------------------------------------------- /test/arangoex/graph/traversal_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Graph.TraversalTest do 2 | # alias Arangoex.Graph.Traversal 3 | 4 | use ExUnit.Case, async: true 5 | 6 | # todo - Traversal tests 7 | end 8 | -------------------------------------------------------------------------------- /lib/arangoex/cursor.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Cursor do 2 | @moduledoc false 3 | 4 | # POST /_api/cursor 5 | # PUT /_api/cursor/{cursor-identifier} 6 | # DELETE /_api/cursor/{cursor-identifier} 7 | 8 | end 9 | -------------------------------------------------------------------------------- /lib/arangoex/bulk.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Bulk do 2 | @moduledoc false 3 | 4 | # POST /_api/import#document 5 | # POST /_api/import#json 6 | # POST /_api/batch 7 | # POST /_api/export 8 | 9 | end 10 | -------------------------------------------------------------------------------- /lib/arangoex/agency.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Agency do 2 | @moduledoc false 3 | 4 | # GET /_api/agency/config 5 | # GET /read 6 | # GET /write 7 | 8 | # todo - implement - insufficient documentation 9 | end 10 | -------------------------------------------------------------------------------- /lib/arangoex/wal.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.WAL do 2 | @moduledoc false 3 | 4 | # PUT /_admin/wal/flush 5 | # GET /_admin/wal/properties 6 | # PUT /_admin/wal/properties 7 | # GET /_admin/wal/transactions 8 | 9 | end 10 | -------------------------------------------------------------------------------- /lib/arangoex/job.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Job do 2 | @moduledoc false 3 | 4 | # PUT /_api/job/{job-id} 5 | # PUT /_api/job/{job-id}/cancel 6 | # DELETE /_api/job/{type} 7 | # GET /_api/job/{job-id} 8 | # GET /_api/job/{type} 9 | 10 | end 11 | -------------------------------------------------------------------------------- /test/arangoex_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ArangoexTest do 2 | use ExUnit.Case, async: true 3 | 4 | test "start_link() connects to the database" do 5 | {:ok, conn} = Arangoex.start_link() 6 | assert is_pid(conn) 7 | end 8 | 9 | # todo - test start_link() with opts 10 | # todo - test request 11 | end 12 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | # Connect to the database. 2 | Arangoex.start_link(name: :arango) 3 | 4 | # Remove the test database if it already exists. 5 | Arangoex.Database.remove(:arango, "test", database: "_system") 6 | 7 | # Create the test database. 8 | Arangoex.Database.create(:arango, %{name: "test"}, database: "_system") 9 | 10 | # Run the tests. 11 | ExUnit.start() 12 | -------------------------------------------------------------------------------- /lib/arangoex/cluster.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Cluster do 2 | @moduledoc false 3 | 4 | # GET /_admin/cluster-test 5 | # POST /_admin/cluster-test 6 | # PUT /_admin/cluster-test 7 | # DELETE /_admin/cluster-test 8 | # PATCH /_admin/cluster-test 9 | # HEAD /_admin/cluster-test 10 | # GET /_admin/clusterCheckPort 11 | # GET /_admin/clusterStatistics 12 | 13 | end 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elixir 2 | 3 | elixir: 4 | - 1.4 5 | - 1.5 6 | 7 | otp_release: 8 | - 19.0 9 | - 20.0 10 | 11 | env: 12 | - ARANGODB_VERSION=3.2 ARANGO_VERSION=30200 13 | 14 | before_install: 15 | - curl https://download.arangodb.com/travisCI/setup_arangodb_${ARANGODB_VERSION}.sh | bash 16 | 17 | script: 18 | - MIX_ENV=test mix credo 19 | - mix test 20 | 21 | after_script: 22 | - kill -9 $(pgrep arangod) 23 | -------------------------------------------------------------------------------- /test/arangoex/connection_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.ConnectionTest do 2 | alias Arangoex.Connection, as: AConnection 3 | 4 | use ExUnit.Case, async: true 5 | 6 | test "start_link() connects to the database" do 7 | args = [host: :localhost, port: 8529, database: "test", username: "root", password: ""] 8 | {:ok, conn} = AConnection.start_link(args) 9 | assert is_pid(conn) 10 | end 11 | 12 | # todo - thorough connection tests 13 | end 14 | -------------------------------------------------------------------------------- /lib/arangoex/transaction.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Transaction do 2 | @moduledoc false 3 | 4 | # alias Arangoex.JSON 5 | 6 | # use Arangoex, base_url: ["/", "_api", "/", "transaction"] 7 | 8 | # POST /_api/transaction 9 | # Execute a transaction. 10 | def execute(%{} = transaction, opts \\ []) do 11 | # {:ok, body} = JSON.encode(transaction) 12 | # 13 | # [] 14 | # |> build_url(opts) 15 | # |> Arangoex.post(body, opts) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/arangoex/graph/traversal.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Graph.Traversal do 2 | @moduledoc false 3 | 4 | # alias Arangoex.JSON 5 | 6 | # use Arangoex, base_url: ["/", "_api", "/", "traversal"] 7 | 8 | # POST /_api/traversal 9 | # Perform a graph traversal starting from a single vertex. 10 | def traverse(%{} = traversal, opts \\ []) do 11 | # {:ok, body} = JSON.encode(traversal) 12 | # 13 | # [] 14 | # |> build_url(opts) 15 | # |> Arangoex.post(body, opts) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "mix" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | insecure-external-code-execution: deny 13 | -------------------------------------------------------------------------------- /lib/arangoex/aql.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.AQL do 2 | @moduledoc false 3 | 4 | # DELETE /_api/cursor/{cursor-identifier} 5 | # POST /_api/query 6 | # GET /_api/query/properties 7 | # PUT /_api/query/properties 8 | # GET /_api/query/current 9 | # GET /_api/query/slow 10 | # DELETE /_api/query/slow 11 | # DELETE /_api/query/{query-id} 12 | # DELETE /_api/query-cache 13 | # GET /_api/query-cache/properties 14 | # PUT /_api/query-cache/properties 15 | # POST /_api/aqlfunction 16 | # DELETE /_api/aqlfunction/{name} 17 | # GET /_api/aqlfunction 18 | 19 | end 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc 12 | 13 | # If the VM crashes, it generates a dump, let's ignore it too. 14 | erl_crash.dump 15 | 16 | # Also ignore archive artifacts (built via "mix archive.build"). 17 | *.ez 18 | 19 | Ignore the dev config 20 | /config/dev.exs 21 | 22 | # Ignore the tmp directory 23 | /tmp 24 | -------------------------------------------------------------------------------- /lib/arangoex/index.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Index do 2 | @moduledoc false 3 | 4 | # GET /_api/index/{index-handle} 5 | # POST /_api/index#general 6 | # DELETE /_api/index/{index-handle} 7 | # GET /_api/index 8 | # POST /_api/index#hash 9 | # POST /_api/index#skiplist 10 | # POST /_api/index#persistent 11 | # POST /_api/index#geo 12 | # POST /_api/index#fulltext 13 | 14 | # covered by Arangoex.Simple 15 | # PUT /_api/simple/by-example 16 | # PUT /_api/simple/first-example 17 | # PUT /_api/simple/near 18 | # PUT /_api/simple/within 19 | # PUT /_api/simple/fulltext 20 | 21 | end 22 | -------------------------------------------------------------------------------- /lib/arangoex/response.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Response do 2 | @moduledoc """ 3 | Response struct and supporting functions for ArangoDB reponses. 4 | """ 5 | 6 | defstruct body: nil, headers: nil, response_type: nil, status_code: nil, version: nil 7 | 8 | @type t :: %__MODULE__{body: any, headers: any, response_type: integer, status_code: integer, version: integer} 9 | 10 | # todo - meta and headers? 11 | def build_response([version, response_type, status_code, _meta] = _header, body) do 12 | %__MODULE__{body: body, response_type: response_type, status_code: status_code, version: version} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/arangoex/response_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.ResponseTest do 2 | alias Arangoex.Response 3 | 4 | use ExUnit.Case, async: true 5 | 6 | test "Response struct" do 7 | response = %Response{} 8 | assert response.body == nil 9 | assert response.headers == nil 10 | assert response.response_type == nil 11 | assert response.status_code == nil 12 | assert response.version == nil 13 | end 14 | 15 | test "build_response() without headers" do 16 | response = Response.build_response([1, 2, 200], %{"foo" => "bar"}) 17 | assert response == %Response{body: %{"foo" => "bar"}, headers: nil, response_type: 2, status_code: 200, version: 1} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/arangoex/replication.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Replication do 2 | @moduledoc false 3 | 4 | # GET /_api/replication/inventory 5 | # GET /_api/replication/dump 6 | # PUT /_api/replication/sync 7 | # GET /_api/replication/clusterInventory 8 | # GET /_api/replication/logger-state 9 | # GET /_api/replication/logger-follow 10 | # GET /_api/replication/logger-first-tick 11 | # GET /_api/replication/logger-tick-ranges 12 | # GET /_api/replication/applier-config 13 | # PUT /_api/replication/applier-config 14 | # PUT /_api/replication/applier-start 15 | # PUT /_api/replication/applier-stop 16 | # GET /_api/replication/applier-state 17 | # PUT /_api/replication/make-slave 18 | # GET /_api/replication/server-id 19 | # DELETE /_api/replication/batch/{id} 20 | # POST /_api/replication/batch 21 | # PUT /_api/replication/batch/{id} 22 | 23 | end 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arangoex 2 | 3 | [![Build Status](https://travis-ci.org/austinsmorris/arangoex.svg?branch=master)](https://travis-ci.org/austinsmorris/arangoex) 4 | 5 | An elixir driver for ArangoDB. 6 | 7 | ## Installation 8 | 9 | The package can be installed from [Hex](https://hex.pm/packages/arangoex): 10 | 11 | 1. Add `arangoex` to your list of dependencies in `mix.exs`: 12 | 13 | ```elixir 14 | def deps do 15 | [{:arangoex, "~> 0.0.1"}] 16 | end 17 | ``` 18 | 19 | 2. Configure `arangoex` for your specific environment: 20 | 21 | ```elixir 22 | config :arangoex, 23 | host: "http://localhost:8529", 24 | database: "myDatabase", 25 | username: "myUsername", 26 | password: "myPassword" 27 | ``` 28 | 29 | 3. Ensure `arangoex` is started before your application: 30 | 31 | ```elixir 32 | def application do 33 | [applications: [:arangoex]] 34 | end 35 | ``` 36 | 37 | ## Running Tests 38 | 39 | You can run tests with `mix test`, but you must startup an instance of 40 | arangodb without authentication. Using 41 | the [official docker image](https://hub.docker.com/_/arangodb/) is 42 | recommended. 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Austin S. Morris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure for your application as: 12 | # 13 | # config :arangoex, key: :value 14 | # 15 | # And access this configuration in your application as: 16 | # 17 | # Application.get_env(:arangoex, :key) 18 | # 19 | # Or configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | import_config "#{Mix.env}.exs" 31 | -------------------------------------------------------------------------------- /lib/arangoex/graph/edges.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Graph.Edges do 2 | @moduledoc """ 3 | This module is for reading inbound and/or outbound edges from a vertex in a graph. 4 | """ 5 | 6 | # use Arangoex, base_url: ["/", "_api", "/", "edges"] 7 | 8 | @doc """ 9 | Returns an array of edges in the collection identified by `collection_name` to or from the vertex identified by 10 | `vertex_handle`. If the direction is not specified, `"any"` edges are returned. 11 | 12 | ## Endpoint 13 | 14 | `GET /_api/edges/{collection-id}` 15 | 16 | ## Options 17 | 18 | * `:direction` - Filters the resulting edges by the direction to or from the given vertex. It may be one of 19 | `"in"`, `"out"`, or `"any"`. 20 | 21 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 22 | 23 | ## Examples 24 | 25 | {:ok, resp} = Arangoex.Graph.Edges.list("my_edges", "my_vertex/12345", direction: "in") 26 | """ 27 | def list(collection_name, vertex_handle, opts \\ []) do 28 | # query_params = [:vertex, :direction] 29 | # opts = Keyword.put_new(opts, :vertex, vertex_handle) 30 | # 31 | # collection_name 32 | # |> build_url(Keyword.put_new(opts, :query_params, query_params)) 33 | # |> Arangoex.get(opts) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :arangoex, 7 | version: "0.1.0", 8 | elixir: "~> 1.4", 9 | build_embedded: Mix.env == :prod, 10 | start_permanent: Mix.env == :prod, 11 | name: "Arangoex", 12 | deps: deps(), 13 | description: description(), 14 | package: package(), 15 | ] 16 | end 17 | 18 | # Configuration for the OTP application 19 | # 20 | # Type "mix help compile.app" for more information 21 | def application do 22 | # Specify extra applications you'll use from Erlang/Elixir 23 | [extra_applications: [:logger]] 24 | end 25 | 26 | # Dependencies can be Hex packages: 27 | # 28 | # {:mydep, "~> 0.3.0"} 29 | # 30 | # Or git/path repositories: 31 | # 32 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 33 | # 34 | # Type "mix help deps" for more examples and options 35 | defp deps do 36 | [ 37 | {:connection, "~> 1.0"}, 38 | {:credo, "~> 0.8", only: [:dev, :test]}, 39 | {:ex_doc, "~> 0.16", only: :dev}, 40 | {:velocy_pack, "~> 0.1"}, 41 | {:velocy_stream, "~> 0.0"}, 42 | ] 43 | end 44 | 45 | defp description do 46 | """ 47 | An Elixir driver for ArangoDB. 48 | """ 49 | end 50 | 51 | defp package do 52 | [ 53 | files: ["config", "lib", "test", ".gitignore", ".travis.yml", "LICENSE*", "mix.exs", "README*"], 54 | maintainers: ["Austin S. Morris"], 55 | licenses: ["MIT"], 56 | links: %{"GitHub" => "https://github.com/austinsmorris/arangoex"}, 57 | ] 58 | end 59 | end 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/arangoex.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex do 2 | @moduledoc false 3 | # todo - document "Shared Options", incl. "database" 4 | 5 | alias Arangoex.Connection, as: AConnection 6 | 7 | @default_opts %{host: :localhost, port: 8529, database: "_system", username: "root", password: ""} 8 | 9 | def request(conn, method, path, params \\ %{}, meta \\ %{}, body \\ nil, opts \\ []) do 10 | # credo:disable-for-previous-line Credo.Check.Refactor.FunctionArity 11 | AConnection.request(conn, method, path, params, meta, body, opts) 12 | end 13 | 14 | def start_link(opts \\ []) do 15 | with {host, opts} <- pop_host(opts), 16 | {port, opts} <- pop_port(opts), 17 | {database, opts} <- pop_database(opts), 18 | {username, opts} <- pop_username(opts), 19 | {password, opts} <- pop_password(opts) 20 | do 21 | AConnection.start_link([host: host, port: port, database: database, username: username, password: password], opts) 22 | else 23 | error -> {:error, error} 24 | end 25 | end 26 | 27 | defp pop_database(opts) do 28 | Keyword.pop(opts, :database, Application.get_env(:arangoex, :database, @default_opts.database)) 29 | end 30 | 31 | defp pop_host(opts), do: Keyword.pop(opts, :host, Application.get_env(:arangoex, :host, @default_opts.host)) 32 | 33 | defp pop_password(opts) do 34 | Keyword.pop(opts, :password, Application.get_env(:arangoex, :password, @default_opts.password)) 35 | end 36 | 37 | defp pop_port(opts), do: Keyword.pop(opts, :port, Application.get_env(:arangoex, :port, @default_opts.port)) 38 | 39 | defp pop_username(opts) do 40 | Keyword.pop(opts, :username, Application.get_env(:arangoex, :username, @default_opts.username)) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, 3 | "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, 4 | "credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, 6 | "ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 8 | "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, 10 | "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, 11 | "velocy_pack": {:hex, :velocy_pack, "0.1.3", "3df81c3040acc40ce811e8ad929ae3d4d776926bb3d450f0079d09153cc87b6f", [:mix], [], "hexpm"}, 12 | "velocy_stream": {:hex, :velocy_stream, "0.0.1", "38fce629d1d47de54c799940546bf3d84d544a140f26c79f7a5cec2474705f58", [:mix], [], "hexpm"}, 13 | } 14 | -------------------------------------------------------------------------------- /test/arangoex/database_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.DatabaseTest do 2 | alias Arangoex.Database 3 | 4 | use ExUnit.Case, async: false 5 | 6 | setup do 7 | # make sure "foo" database does not exist for tests 8 | Database.remove(:arango, "foo", database: "_system") 9 | 10 | on_exit fn -> 11 | # make sure "foo" database is removed from any test 12 | Database.remove(:arango, "foo", database: "_system") 13 | end 14 | end 15 | 16 | test "create() creates a new database." do 17 | {:ok, resp} = Database.create(:arango, %{name: "foo"}, database: "_system") 18 | 19 | assert resp.status_code == 201 20 | assert resp.body["code"] == 201 21 | assert resp.body["error"] === false 22 | assert resp.body["result"] === true 23 | 24 | {:ok, list_resp} = Database.list(:arango) 25 | 26 | assert Enum.member?(list_resp.body["result"], "foo") 27 | end 28 | 29 | test "current() returns info about current database." do 30 | {:ok, resp} = Database.current(:arango) 31 | 32 | assert resp.status_code == 200 33 | assert resp.body["code"] == 200 34 | assert resp.body["error"] === false 35 | assert is_binary(resp.body["result"]["id"]) 36 | assert resp.body["result"]["isSystem"] === false 37 | assert resp.body["result"]["name"] === "test" 38 | assert is_binary(resp.body["result"]["path"]) 39 | end 40 | 41 | test "list() returns information about databases in the system." do 42 | {:ok, resp} = Database.list(:arango) 43 | 44 | assert resp.status_code == 200 45 | assert resp.body["code"] == 200 46 | assert resp.body["error"] === false 47 | assert is_list(resp.body["result"]) 48 | assert Enum.member?(resp.body["result"], "test") 49 | assert Enum.member?(resp.body["result"], "_system") 50 | end 51 | 52 | test "list_for_current_user() returns information about databases for the current user." do 53 | {:ok, resp} = Database.list_for_current_user(:arango) 54 | 55 | assert resp.status_code == 200 56 | assert resp.body["code"] == 200 57 | assert resp.body["error"] === false 58 | assert is_list(resp.body["result"]) 59 | assert Enum.member?(resp.body["result"], "test") 60 | assert Enum.member?(resp.body["result"], "_system") 61 | end 62 | 63 | test "remove() deletes the given database." do 64 | Database.create(:arango, %{name: "foo"}, database: "_system") 65 | {:ok, resp} = Database.remove(:arango, "foo", database: "_system") 66 | 67 | assert resp.status_code == 200 68 | assert resp.body["code"] == 200 69 | assert resp.body["result"] === true 70 | assert resp.body["error"] === false 71 | 72 | {:ok, list_resp} = Database.list(:arango) 73 | 74 | refute Enum.member?(list_resp.body["result"], "foo") 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /lib/arangoex/database.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Database do 2 | @moduledoc """ 3 | This module contains functions used to manage databases. 4 | """ 5 | 6 | @doc """ 7 | Create a new database. 8 | 9 | The `conn` parameter is an ArangoDB connection PID. The `database` parameter is a map describing the database to be 10 | created. 11 | 12 | ## Endpoint 13 | 14 | POST /_api/database 15 | 16 | ## Options 17 | 18 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 19 | 20 | ## Examples 21 | 22 | {:ok, conn} = Arangoex.start_link() 23 | {:ok, resp} = Arangoex.Database.create(conn, %{name: "foo"}) 24 | """ 25 | def create(conn, %{} = database, opts \\ []) do 26 | Arangoex.request(conn, :post, "/_api/database", %{}, %{}, database, opts) 27 | end 28 | 29 | @doc """ 30 | Return information about the current database. 31 | 32 | The `conn` parameter is an ArangoDB connection PID. 33 | 34 | ## Endpoint 35 | 36 | GET /_api/database/current 37 | 38 | ## Options 39 | 40 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 41 | 42 | ## Examples 43 | 44 | {:ok, conn} = Arangoex.start_link() 45 | {:ok, resp} = Arangoex.Database.current(conn) 46 | """ 47 | def current(conn, opts \\ []) do 48 | Arangoex.request(conn, :get, "/_api/database/current", %{}, %{}, nil, opts) 49 | end 50 | 51 | @doc """ 52 | Return a list of databases on the system. 53 | 54 | The `conn` parameter is an ArangoDB connection PID. 55 | 56 | ## Endpoint 57 | 58 | GET /_api/database 59 | 60 | ## Options 61 | 62 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 63 | 64 | ## Examples 65 | 66 | {:ok, conn} = Arangoex.start_link() 67 | {:ok, resp} = Arangoex.Database.list(conn) 68 | """ 69 | def list(conn, opts \\ []) do 70 | Arangoex.request(conn, :get, "/_api/database", %{}, %{}, nil, opts) 71 | end 72 | 73 | @doc """ 74 | Return a list of databases on the system for the current user. 75 | 76 | The `conn` parameter is an ArangoDB connection PID. 77 | 78 | ## Endpoint 79 | 80 | GET /_api/database/user 81 | 82 | ## Options 83 | 84 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 85 | 86 | ## Examples 87 | 88 | {:ok, conn} = Arangoex.start_link() 89 | {:ok, resp} = Arangoex.Database.list_for_current_user(conn) 90 | """ 91 | def list_for_current_user(conn, opts \\ []) do 92 | Arangoex.request(conn, :get, "/_api/database/user", %{}, %{}, nil, opts) 93 | end 94 | 95 | @doc """ 96 | Remove the given database from the system. 97 | 98 | The `conn` parameter is an ArangoDB connection PID. The `database_name` parameter is the database to be removed. 99 | 100 | ## Endpoint 101 | 102 | DELETE /_api/database/{database_name} 103 | 104 | ## Options 105 | 106 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 107 | 108 | ## Examples 109 | 110 | {:ok, conn} = Arangoex.start_link() 111 | {:ok, resp} = Arangoex.Database.remove(conn, "foo") 112 | """ 113 | def remove(conn, database_name, opts \\ []) do 114 | Arangoex.request(conn, :delete, "/_api/database/#{database_name}", %{}, %{}, nil, opts) 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/arangoex/document.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Document do 2 | @moduledoc false 3 | 4 | # alias Arangoex.JSON 5 | alias Arangoex.Simple 6 | 7 | # use Arangoex, base_url: ["/", "_api", "/", "document"] 8 | 9 | defdelegate list(collection, attrs \\ [], opts \\ []), to: Simple, as: :list_documents 10 | defdelegate list_keys(collection, type \\ "path", opts \\ []), to: Simple, as: :list_keys 11 | 12 | # POST /_api/document/{collection} 13 | # Create a new document. 14 | def create(collection, document, opts \\ []) do 15 | # todo - implement waitForSync, returnNew, silent, collection (< v3.0?) 16 | # {:ok, body} = JSON.encode(document) 17 | # 18 | # collection 19 | # |> build_url(opts) 20 | # |> Arangoex.post(body, opts) 21 | end 22 | 23 | # GET /_api/document/{document-handle} 24 | # Returns the document identified by document-handle. 25 | def get(document_handle, opts \\ []) do 26 | # todo - implement If-None-Match and If-Match headers 27 | # document_handle 28 | # |> build_url(opts) 29 | # |> Arangoex.get(opts) 30 | end 31 | 32 | # HEAD /_api/document/{document-handle} 33 | # Returns the header of the document identified by document-handle. 34 | def info(document_handle, opts \\ []) do 35 | # todo - implement If-None-Match and If-Match headers 36 | # document_handle 37 | # |> build_url(opts) 38 | # |> Arangoex.head(opts) 39 | end 40 | 41 | # PUT /_api/document/{document-handle} 42 | # Replace the document identified by document-handle. 43 | def replace(document_handle, document, opts \\ []) do 44 | # todo - implement waitForSync, ignoreRevs, returnOld, returnNew, and silent query parameters 45 | # todo - If-Match header 46 | # {:ok, body} = JSON.encode(document) 47 | # 48 | # document_handle 49 | # |> build_url(opts) 50 | # |> Arangoex.put(body, opts) 51 | end 52 | 53 | # PUT /_api/document/{collection} 54 | # Replace multiple documents in the collection. 55 | def replace_many(__opts \\ []) do 56 | # todo - implement - need documentation examples 57 | # todo - implement waitForSync, ignoreRevs, returnOld, and returnNew query parameters 58 | end 59 | 60 | # DELETE /_api/document/{document-handle} 61 | # Remove the document identified by document-handle. 62 | def remove(document_handle, opts \\ []) do 63 | # todo - implement waitForSync, returnOld, and silent query parameters 64 | # todo - implement If-Match header 65 | # document_handle 66 | # |> build_url(opts) 67 | # |> Arangoex.delete(opts) 68 | end 69 | 70 | # DELETE /_api/document/{collection} 71 | # Remove multiple documents in the collection. 72 | def remove_many(__opts \\ []) do 73 | # todo - implement - documentation is inconsistent 74 | # todo - implement waitForSync, returnOld, and ignoreRevs query parameters 75 | end 76 | 77 | # PATCH /_api/document/{document-handle} 78 | # Update the document identified by document-handle. 79 | def update(__opts \\ []) do 80 | # todo - implement 81 | # todo - implement keepNull, mergeObjects, waitForSync, ignoreRevs, returnOld, returnNew, & silent query parameters 82 | # todo - implement If-Match header 83 | end 84 | 85 | # PATCH /_api/document/{collection} 86 | # Update multiple documents in the collection. 87 | def update_many(__opts \\ []) do 88 | # todo - implement - need documentation examples 89 | # todo - implement keepNull, mergeObjects, waitForSync, ingoreRevs, returnOld, and returnNew query parameters 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/arangoex/connection.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Connection do 2 | @moduledoc false 3 | 4 | alias Arangoex.Response 5 | 6 | use Connection 7 | 8 | @default_timeout 5_000 9 | @message_type_authorization 1000 10 | @message_type_request 1 11 | @version 1 12 | 13 | # public api 14 | def request(conn, method, path, params, meta, body, opts) do 15 | # credo:disable-for-previous-line Credo.Check.Refactor.FunctionArity 16 | # todo - allow for timeout to be passed in opts 17 | Connection.call(conn, {:request, method, path, params, meta, body, opts}, @default_timeout) 18 | end 19 | 20 | def start_link(args, opts \\ []) do 21 | Connection.start_link(__MODULE__, args, opts) 22 | end 23 | 24 | # callbacks 25 | def init(args) do 26 | {:connect, args, %{database: Keyword.get(args, :database), from: nil, socket: nil}} 27 | end 28 | 29 | def connect(info, state) do 30 | # todo - make sure host is valid for :gen_tcp 31 | host = Keyword.get(info, :host) 32 | port = Keyword.get(info, :port) 33 | username = Keyword.get(info, :username) 34 | password = Keyword.get(info, :password) 35 | 36 | # todo - error handling for encoding 37 | auth = [version(), message_type_authorization(), "plain", username, password] 38 | |> VelocyPack.encode_to_iodata!() 39 | |> VelocyStream.pack() 40 | 41 | # todo = error handling 42 | {:ok, socket} = :gen_tcp.connect(host, port, [packet: :raw, mode: :binary, active: false], @default_timeout) 43 | :ok = :gen_tcp.send(socket, "VST/1.0\r\n\r\n") 44 | :ok = :gen_tcp.send(socket, auth) 45 | 46 | # todo - get auth response and return any possible error 47 | # todo - handle receiveing complete auth response without sleep 48 | :timer.sleep(100) 49 | :gen_tcp.recv(socket, 0) 50 | 51 | :ok = :inet.setopts(socket, active: :once) 52 | {:ok, %{state | socket: socket}} 53 | end 54 | 55 | def handle_call({:request, method, path, params, meta, body, opts}, from, state) do 56 | header = [ 57 | version(), 58 | message_type_request(), 59 | Keyword.get(opts, :database, state.database), 60 | request_type(method), 61 | path, 62 | params, 63 | meta 64 | ] 65 | 66 | # todo - use message id for VelocyStream 67 | request = header |> build_request(body) |> VelocyStream.pack 68 | 69 | # todo - error handling 70 | :ok = :gen_tcp.send(state.socket, request) 71 | {:noreply, %{state | from: from}} 72 | end 73 | 74 | def handle_info({:tcp, _port, data}, state) do 75 | :ok = :inet.setopts(state.socket, active: :once) 76 | 77 | # todo - get all the data and parse into response 78 | # todo - match message id from VelocyStream 79 | # todo - error handling 80 | {message, 0} = VelocyStream.unpack(data) 81 | {:ok, header, tail} = VelocyPack.decode(message) 82 | {:ok, body} = parse_body(tail) 83 | 84 | # todo - verify same version 85 | # todo - response type 2 or 3? 86 | # todo - status codes match? 87 | # todo - :error for non-ok responses? 88 | response = {:ok, Response.build_response(header, body)} 89 | 90 | # todo - what is state is nil? 91 | if state.from !== nil do 92 | Connection.reply(state.from, response) 93 | end 94 | 95 | {:noreply, %{state | from: nil}} 96 | end 97 | 98 | defp build_request(header, nil), do: VelocyPack.encode_to_iodata!(header) 99 | defp build_request(header, body), do: [VelocyPack.encode_to_iodata!(header), VelocyPack.encode_to_iodata!(body)] 100 | 101 | defp message_type_authorization, do: @message_type_authorization 102 | defp message_type_request, do: @message_type_request 103 | 104 | defp parse_body(""), do: {:ok, nil, ""} 105 | defp parse_body(data), do: VelocyPack.decode(data) 106 | 107 | defp request_type(:delete), do: 0 108 | defp request_type(:get), do: 1 109 | defp request_type(:post), do: 2 110 | defp request_type(:put), do: 3 111 | defp request_type(:head), do: 4 112 | defp request_type(:patch), do: 5 113 | defp request_type(:options), do: 6 114 | 115 | defp version, do: @version 116 | end 117 | -------------------------------------------------------------------------------- /test/arangoex/user_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.UserTest do 2 | alias Arangoex.User 3 | 4 | use ExUnit.Case, async: false 5 | setup do 6 | # make sure "foo" user does not exist for tests 7 | User.remove(:arango, "foo") 8 | 9 | on_exit fn -> 10 | # make sure "foo" is removed from any test 11 | User.remove(:arango, "foo") 12 | end 13 | end 14 | 15 | test "create() creates a new user." do 16 | {:ok, resp} = User.create(:arango, %{user: "foo"}) 17 | 18 | assert resp.status_code === 201 19 | assert resp.body["code"] === 201 20 | assert resp.body["error"] === false 21 | assert resp.body["active"] === true 22 | assert resp.body["extra"] === %{} 23 | assert resp.body["user"] === "foo" 24 | 25 | foo = %{"active" => true, "extra" => %{}, "user" => "foo"} 26 | {:ok, list_resp} = User.list(:arango) 27 | 28 | assert Enum.member?(list_resp.body["result"], foo) 29 | end 30 | 31 | test "database() shows information about databases a user has access to." do 32 | User.create(:arango, %{user: "foo"}) 33 | User.grant(:arango, "foo", "test") 34 | {:ok, resp} = User.database(:arango, "foo") 35 | 36 | assert resp.status_code === 200 37 | assert resp.body["code"] === 200 38 | assert resp.body["error"] === false 39 | assert resp.body["result"] === %{"test" => "rw"} 40 | end 41 | 42 | test "get() show information about a user." do 43 | User.create(:arango, %{user: "foo"}) 44 | {:ok, resp} = User.get(:arango, "foo") 45 | 46 | assert resp.status_code === 200 47 | assert resp.body["code"] === 200 48 | assert resp.body["error"] === false 49 | assert resp.body["active"] === true 50 | assert resp.body["extra"] === %{} 51 | assert resp.body["user"] === "foo" 52 | end 53 | 54 | test "grant() grants database access to a user." do 55 | User.create(:arango, %{user: "foo"}) 56 | User.grant(:arango, "foo", "test") 57 | {:ok, resp} = User.database(:arango, "foo") 58 | 59 | assert resp.status_code === 200 60 | assert resp.body["code"] === 200 61 | assert resp.body["error"] === false 62 | assert resp.body["result"] === %{"test" => "rw"} 63 | end 64 | 65 | test "list() shows users in the system." do 66 | User.create(:arango, %{user: "foo"}) 67 | {:ok, resp} = User.list(:arango) 68 | 69 | assert resp.status_code === 200 70 | assert resp.body["code"] === 200 71 | assert resp.body["error"] === false 72 | 73 | foo = %{"active" => true, "extra" => %{}, "user" => "foo"} 74 | assert Enum.member?(resp.body["result"], foo) 75 | end 76 | 77 | test "remove() removes a user from the system." do 78 | User.create(:arango, %{user: "foo"}) 79 | {:ok, resp} = User.remove(:arango, "foo") 80 | 81 | assert resp.status_code === 202 82 | assert resp.body["code"] === 202 83 | assert resp.body["error"] === false 84 | 85 | {:ok, list_resp} = User.list(:arango) 86 | 87 | foo = %{"active" => true, "extra" => %{}, "user" => "foo"} 88 | refute Enum.member?(list_resp.body["result"], foo) 89 | end 90 | 91 | test "replace() replaces the properties of a user." do 92 | User.create(:arango, %{user: "foo"}) 93 | {:ok, resp} = User.replace(:arango, "foo", %{active: false}) 94 | 95 | assert resp.status_code === 200 96 | assert resp.body["code"] === 200 97 | assert resp.body["error"] === false 98 | assert resp.body["active"] === false 99 | assert resp.body["extra"] === %{} 100 | assert resp.body["user"] === "foo" 101 | 102 | {:ok, list_resp} = User.list(:arango) 103 | 104 | foo = %{"active" => false, "extra" => %{}, "user" => "foo"} 105 | assert Enum.member?(list_resp.body["result"], foo) 106 | end 107 | 108 | test "revoke() revokes database access from a user." do 109 | User.create(:arango, %{user: "foo"}) 110 | User.grant(:arango, "foo", "test") 111 | {:ok, resp} = User.database(:arango, "foo") 112 | 113 | assert resp.status_code === 200 114 | assert resp.body["code"] === 200 115 | assert resp.body["error"] === false 116 | assert resp.body["result"] === %{"test" => "rw"} 117 | 118 | User.revoke(:arango, "foo", "test") 119 | {:ok, db_resp} = User.database(:arango, "foo") 120 | 121 | assert db_resp.status_code === 200 122 | assert db_resp.body["code"] === 200 123 | assert db_resp.body["error"] === false 124 | assert db_resp.body["result"] === %{} 125 | end 126 | 127 | test "update() updates the properties of a user." do 128 | User.create(:arango, %{user: "foo"}) 129 | {:ok, resp} = User.replace(:arango, "foo", %{active: false}) 130 | 131 | assert resp.status_code === 200 132 | assert resp.body["code"] === 200 133 | assert resp.body["error"] === false 134 | assert resp.body["active"] === false 135 | assert resp.body["extra"] === %{} 136 | assert resp.body["user"] === "foo" 137 | 138 | {:ok, list_resp} = User.list(:arango) 139 | 140 | foo = %{"active" => false, "extra" => %{}, "user" => "foo"} 141 | assert Enum.member?(list_resp.body["result"], foo) 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /test/arangoex/simple_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.SimpleTest do 2 | # alias Arangoex.Simple 3 | 4 | use ExUnit.Case, async: false 5 | 6 | # todo Simple tests 7 | 8 | # alias Arangoex.{ 9 | # Collection, 10 | # Document, 11 | # JSON, 12 | # Simple 13 | # } 14 | 15 | # @collection_name "cars" 16 | # 17 | # @document1 %{brand: "Ford", model: "Festiva"} 18 | # @document2 %{brand: "Toyota", model: "Camry"} 19 | # 20 | # @example %{brand: "Ford"} 21 | 22 | setup do 23 | # Collection.create(%{name: @collection_name}) 24 | # 25 | # {:ok, response1} = Document.create(@collection_name, @document1) 26 | # {:ok, response2} = Document.create(@collection_name, @document2) 27 | # 28 | # on_exit fn -> 29 | # Collection.remove(@collection_name) 30 | # end 31 | # 32 | # {:ok, responses: [response1, response2]} 33 | end 34 | 35 | # test "get_first_document_by_example() gets first document by example from a collection" do 36 | # {:ok, response} = Simple.get_first_document_by_example(@collection_name, @example) 37 | # 38 | # body = JSON.decode!(response.body) 39 | # 40 | # assert get_in(body, ["code"]) == 200 41 | # assert get_in(body, ["document", "brand"]) == "Ford" 42 | # assert get_in(body, ["document", "model"]) == "Festiva" 43 | # end 44 | 45 | # test "get_random_document() gets a random document from a collection" do 46 | # {:ok, response} = Simple.get_random_document(@collection_name) 47 | # 48 | # body = JSON.decode!(response.body) 49 | # doc = get_in(body, ["document"]) 50 | # 51 | # assert get_in(body, ["code"]) == 200 52 | # assert Map.has_key?(doc, "brand") 53 | # assert Map.has_key?(doc, "model") 54 | # end 55 | 56 | # @example_brands ["Ford", "Toyota"] 57 | # @example_models ["Festiva", "Camry"] 58 | # test "list_documents() gets all documents in a collection" do 59 | # {:ok, response} = Simple.list_documents(@collection_name) 60 | # 61 | # body = JSON.decode!(response.body) 62 | # 63 | # [document1, document2] = get_in(body, ["result"]) 64 | # 65 | # assert Enum.member?(@example_brands, get_in(document1, ["brand"])) 66 | # assert Enum.member?(@example_models, get_in(document1, ["model"])) 67 | # 68 | # assert Enum.member?(@example_brands, get_in(document2, ["brand"])) 69 | # assert Enum.member?(@example_models, get_in(document2, ["model"])) 70 | # end 71 | 72 | # test "list_documents_by_example() gets all documents matched by example document" do 73 | # {:ok, response} = Simple.list_documents_by_example(@collection_name, @example) 74 | # 75 | # body = JSON.decode!(response.body) 76 | # [document] = get_in(body, ["result"]) 77 | # 78 | # assert get_in(document, ["brand"]) == "Ford" 79 | # assert get_in(document, ["model"]) == "Festiva" 80 | # end 81 | 82 | # test "list_documents_for_keys() gets all documents matched by example document", %{responses: [resp1|_]} do 83 | # key = JSON.decode!(resp1.body) |> get_in(["_key"]) 84 | # 85 | # {:ok, response} = Simple.list_documents_for_keys(@collection_name, [key]) 86 | # 87 | # body = JSON.decode!(response.body) 88 | # [document] = get_in(body, ["documents"]) 89 | # 90 | # assert get_in(document, ["brand"]) == "Ford" 91 | # assert get_in(document, ["model"]) == "Festiva" 92 | # end 93 | 94 | # test "list_keys() gets all keys for documents in collection" do 95 | # {:ok, response} = Simple.list_keys(@collection_name, "key") 96 | # 97 | # body = JSON.decode!(response.body) 98 | # keys = get_in(body, ["result"]) 99 | # 100 | # assert Enum.count(keys) == 2 101 | # end 102 | 103 | # test "remove_documents_by_example() removes all documents matched by example document" do 104 | # {:ok, _} = Simple.remove_documents_by_example(@collection_name, @example) 105 | # {:ok, response} = Simple.list_documents(@collection_name) 106 | # 107 | # body = JSON.decode!(response.body) 108 | # [result] = get_in(body, ["result"]) 109 | # 110 | # assert get_in(result, ["brand"]) == "Toyota" 111 | # end 112 | 113 | # test "remove_documents_for_keys() remove all documents match by list of keys", %{responses: responses} do 114 | # keys = Enum.map(responses, fn resp -> JSON.decode!(resp.body) |> get_in(["_key"]) end) 115 | # {:ok, _} = Simple.remove_documents_for_keys(@collection_name, keys) 116 | # {:ok, response} = Simple.list_documents(@collection_name) 117 | # 118 | # assert [] == JSON.decode!(response.body) |> get_in(["result"]) 119 | # end 120 | 121 | # @replaced_brands ["Chevrolet", "Toyota"] 122 | # @replaced_models ["Camaro", "Camry"] 123 | # test "replace_documents_by_example() replaces all documents matched by example by another document" do 124 | # replacement = %{brand: "Chevrolet", model: "Camaro"} 125 | # 126 | # {:ok, _} = Simple.replace_documents_by_example(@collection_name, @example, replacement) 127 | # {:ok, response} = Simple.list_documents(@collection_name) 128 | # 129 | # body = JSON.decode!(response.body) 130 | # 131 | # [document1, document2] = get_in(body, ["result"]) 132 | # 133 | # assert Enum.member?(@replaced_brands, get_in(document1, ["brand"])) 134 | # assert Enum.member?(@replaced_models, get_in(document1, ["model"])) 135 | # 136 | # assert Enum.member?(@replaced_brands, get_in(document2, ["brand"])) 137 | # assert Enum.member?(@replaced_models, get_in(document2, ["model"])) 138 | # end 139 | 140 | # @modified_brands ["Ford", "Toyota"] 141 | # @modified_models ["Ranger", "Camry"] 142 | # test "update_documents_by_example() updates all documents matched by example with another document" do 143 | # modification = %{model: "Ranger"} 144 | # 145 | # {:ok, _} = Simple.update_documents_by_example(@collection_name, @example, modification) 146 | # {:ok, response} = Simple.list_documents(@collection_name) 147 | # 148 | # body = JSON.decode!(response.body) 149 | # 150 | # [document1, document2] = get_in(body, ["result"]) 151 | # 152 | # assert Enum.member?(@modified_brands, get_in(document1, ["brand"])) 153 | # assert Enum.member?(@modified_models, get_in(document1, ["model"])) 154 | # 155 | # assert Enum.member?(@modified_brands, get_in(document2, ["brand"])) 156 | # assert Enum.member?(@modified_models, get_in(document2, ["model"])) 157 | # end 158 | end 159 | -------------------------------------------------------------------------------- /lib/arangoex/admin.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Admin do 2 | @moduledoc false 3 | 4 | # alias Arangoex.JSON 5 | # import Arangoex, only: [get_base_url: 1] 6 | 7 | # use Arangoex, base_url: ["/", "_admin"] 8 | 9 | # POST /_api/tasks 10 | # Create a server task. 11 | def create_task(%{} = task, opts \\ []) do 12 | # {:ok, body} = JSON.encode(task) 13 | # 14 | # [get_base_url(opts), "/", "_api", "/", "tasks"] 15 | # |> Arangoex.post(body, opts) 16 | end 17 | 18 | # PUT /_api/tasks/{id} 19 | # Create a server task with the given id. 20 | def create_task_with_id(task_id, task, opts \\ []) when is_binary(task_id) do 21 | # {:ok, body} = JSON.encode(task) 22 | # 23 | # [get_base_url(opts), "/", "_api", "/", "tasks", "/", task_id] 24 | # |> Arangoex.put(body, opts) 25 | end 26 | 27 | # POST /_admin/execute 28 | # Execute the given program. 29 | def execute(_program, _opts \\ []) do 30 | # todo - implement - insufficient documenation. 31 | end 32 | 33 | # GET /_admin/echo 34 | # Return the current request. 35 | def get_echo(opts \\ []) do 36 | # "echo" 37 | # |> build_url(opts) 38 | # |> Arangoex.get(opts) 39 | end 40 | 41 | # GET /_admin/log 42 | # Return gobal logs from the server. 43 | def get_log(opts \\ []) do 44 | # todo - implement upto, level, start, size, offset, search, and sort query parameters 45 | # "log" 46 | # |> build_url(opts) 47 | # |> Arangoex.get(opts) 48 | end 49 | 50 | # GET /_admin/log/level 51 | # Return the current server log level. 52 | def get_log_level(opts \\ []) do 53 | # ["log", "/", "level"] 54 | # |> build_url(opts) 55 | # |> Arangoex.get(opts) 56 | end 57 | 58 | # GET /_admin/long_echo 59 | # Return the current request and continue. 60 | def get_long_echo(opts \\ []) do 61 | # todo - implement inevitable timeout error response 62 | # "long_echo" 63 | # |> build_url(opts) 64 | # |> Arangoex.get(opts) 65 | end 66 | 67 | # GET /_admin/server/id 68 | # Return the id of the server in the cluster. 69 | def get_server_id(opts \\ []) do 70 | # ["server", "/", "id"] 71 | # |> build_url(opts) 72 | # |> Arangoex.get(opts) 73 | end 74 | 75 | # GET /_admin/server/role 76 | # Return the role of the server in the cluster. 77 | def get_server_role(opts \\ []) do 78 | # ["server", "/", "role"] 79 | # |> build_url(opts) 80 | # |> Arangoex.get(opts) 81 | end 82 | 83 | # GET /_admin/statistics 84 | # Return statistics for the system. 85 | def get_statistics(opts \\ []) do 86 | # "statistics" 87 | # |> build_url(opts) 88 | # |> Arangoex.get(opts) 89 | end 90 | 91 | # GET /_admin/statistics-description 92 | # Return a description of the system statistics. 93 | def get_statistics_description(opts \\ []) do 94 | # "statistics-description" 95 | # |> build_url(opts) 96 | # |> Arangoex.get(opts) 97 | end 98 | 99 | # GET /_admin/time 100 | # Return the system time. 101 | def get_system_time(opts \\ []) do 102 | # ["time"] 103 | # |> build_url(opts) 104 | # |> Arangoex.get(opts) 105 | end 106 | 107 | # GET /_admin/database/target-version 108 | # Return the required database target version. 109 | def get_target_version(opts \\ []) do 110 | # ["database", "/", "target-version"] 111 | # |> build_url(opts) 112 | # |> Arangoex.get(opts) 113 | end 114 | 115 | # GET /_api/tasks/{id} 116 | # Return the server task indicated by the task-id. 117 | def get_task(task_id, opts \\ []) when is_binary(task_id) do 118 | # [get_base_url(opts), "/", "_api", "/", "tasks", "/", task_id] 119 | # |> Arangoex.get(opts) 120 | end 121 | 122 | # GET /_api/version 123 | # Return the server version. 124 | def get_version(opts \\ []) do 125 | # todo - implement details query parameter 126 | # Arangoex.get([get_base_url(opts), "/", "_api", "/", "version"], opts) 127 | end 128 | 129 | # GET /_api/endpoint 130 | # Return a list of endpoints configured for the server. 131 | def list_endpoints(opts \\ []) do 132 | # todo - must use the system db 133 | # Arangoex.get([get_base_url(opts), "/", "_api", "/", "endpoints"], opts) 134 | end 135 | 136 | # GET /_api/tasks/ 137 | # List all tasks on the server. 138 | def list_tasks(opts \\ []) do 139 | # Arangoex.get([get_base_url(opts), "/", "_api" , "/", "tasks"], opts) 140 | end 141 | 142 | # POST /_admin/routing/reload 143 | # Reload the collection routing information. 144 | def reload_routing(opts \\ []) do 145 | # ["routing", "/", "reload"] 146 | # |> build_url(opts) 147 | # |> Arangoex.post("", opts) 148 | end 149 | 150 | # DELETE /_api/tasks/{id} 151 | # Remove the task with the given id from the server. 152 | def remove_task(task_id, opts \\ []) when is_binary(task_id) do 153 | # [get_base_url(opts), "/", "_api", "/", "tasks", "/", task_id] 154 | # |> Arangoex.delete(opts) 155 | end 156 | 157 | # PUT /_admin/log/level 158 | # Set the server log levels. 159 | def set_log_level(%{} = levels \\ %{}, opts \\ []) do 160 | # {:ok, body} = JSON.encode(levels) 161 | # ["log", "/", "level"] 162 | # |> build_url(opts) 163 | # |> Arangoex.put(body, opts) 164 | end 165 | 166 | # DELETE /_admin/shutdown 167 | # Initial a clean shutdown sequence. 168 | def shutdown(opts \\ []) do 169 | # "shutdown" 170 | # |> build_url(opts) 171 | # |> Arangoex.delete(opts) 172 | end 173 | 174 | # GET /_admin/sleep 175 | # Sleep for the given duration in seconds. 176 | def sleep(duration \\ 0, opts \\ []) 177 | def sleep(duration, opts) when is_integer(duration) do 178 | # ["sleep", "?", "duration", "=", Integer.to_string(duration)] 179 | # |> build_url(opts) 180 | # |> Arangoex.get(opts) 181 | end 182 | def sleep(duration, opts) when is_float(duration) do 183 | # ["sleep", "?", "duration", "=", Float.to_string(duration)] 184 | # |> build_url(opts) 185 | # |> Arangoex.get(opts) 186 | end 187 | 188 | # POST /_admin/test 189 | # Run the list of test files on the server. 190 | def test(tests \\ [], opts \\ []) when is_list(tests)do 191 | # {:ok, body} = JSON.encode(%{tests: tests}) 192 | # 193 | # "test" 194 | # |> build_url(opts) 195 | # |> Arangoex.post(body, opts) 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /config/.credo.exs: -------------------------------------------------------------------------------- 1 | # This file contains the configuration for Credo and you are probably reading 2 | # this after creating it with `mix credo.gen.config`. 3 | # 4 | # If you find anything wrong or unclear in this file, please report an 5 | # issue on GitHub: https://github.com/rrrene/credo/issues 6 | # 7 | %{ 8 | # 9 | # You can have as many configs as you like in the `configs:` field. 10 | configs: [ 11 | %{ 12 | # 13 | # Run any exec using `mix credo -C `. If no exec name is given 14 | # "default" is used. 15 | # 16 | name: "default", 17 | # 18 | # These are the files included in the analysis: 19 | files: %{ 20 | # 21 | # You can give explicit globs or simply directories. 22 | # In the latter case `**/*.{ex,exs}` will be used. 23 | # 24 | included: ["config/", "lib/", "test/"], 25 | excluded: [~r"/_build/", ~r"/deps/", ~r"/docs/", ~r"/tmp/"] 26 | }, 27 | # 28 | # If you create your own checks, you must specify the source files for 29 | # them here, so they can be loaded by Credo before running the analysis. 30 | # 31 | requires: [], 32 | # 33 | # If you want to enforce a style guide and need a more traditional linting 34 | # experience, you can change `strict` to `true` below: 35 | # 36 | strict: true, 37 | # 38 | # If you want to use uncolored output by default, you can change `color` 39 | # to `false` below: 40 | # 41 | color: true, 42 | # 43 | # You can customize the parameters of any check by adding a second element 44 | # to the tuple. 45 | # 46 | # To disable a check put `false` as second element: 47 | # 48 | # {Credo.Check.Design.DuplicatedCode, false} 49 | # 50 | checks: [ 51 | {Credo.Check.Consistency.ExceptionNames}, 52 | {Credo.Check.Consistency.LineEndings}, 53 | {Credo.Check.Consistency.ParameterPatternMatching}, 54 | {Credo.Check.Consistency.SpaceAroundOperators}, 55 | {Credo.Check.Consistency.SpaceInParentheses}, 56 | {Credo.Check.Consistency.TabsOrSpaces}, 57 | 58 | # For some checks, like AliasUsage, you can only customize the priority 59 | # Priority values are: `low, normal, high, higher` 60 | # 61 | {Credo.Check.Design.AliasUsage, priority: :low}, 62 | 63 | # For others you can set parameters 64 | 65 | # If you don't want the `setup` and `test` macro calls in ExUnit tests 66 | # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just 67 | # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. 68 | # 69 | {Credo.Check.Design.DuplicatedCode, excluded_macros: []}, 70 | 71 | # You can also customize the exit_status of each check. 72 | # If you don't want TODO comments to cause `mix credo` to fail, just 73 | # set this value to 0 (zero). 74 | # 75 | # todo - check for todos 76 | # {Credo.Check.Design.TagTODO, exit_status: 0}, 77 | {Credo.Check.Design.TagTODO, false}, 78 | {Credo.Check.Design.TagFIXME}, 79 | 80 | {Credo.Check.Readability.FunctionNames}, 81 | {Credo.Check.Readability.LargeNumbers}, 82 | {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120}, 83 | {Credo.Check.Readability.ModuleAttributeNames}, 84 | {Credo.Check.Readability.ModuleDoc}, 85 | {Credo.Check.Readability.ModuleNames}, 86 | {Credo.Check.Readability.ParenthesesOnZeroArityDefs}, 87 | {Credo.Check.Readability.ParenthesesInCondition}, 88 | {Credo.Check.Readability.PredicateFunctionNames}, 89 | {Credo.Check.Readability.PreferImplicitTry}, 90 | {Credo.Check.Readability.RedundantBlankLines}, 91 | {Credo.Check.Readability.StringSigils}, 92 | {Credo.Check.Readability.TrailingBlankLine}, 93 | {Credo.Check.Readability.TrailingWhiteSpace}, 94 | {Credo.Check.Readability.VariableNames}, 95 | {Credo.Check.Readability.Semicolons}, 96 | {Credo.Check.Readability.SpaceAfterCommas}, 97 | 98 | {Credo.Check.Refactor.DoubleBooleanNegation}, 99 | {Credo.Check.Refactor.CondStatements}, 100 | {Credo.Check.Refactor.CyclomaticComplexity}, 101 | {Credo.Check.Refactor.FunctionArity}, 102 | {Credo.Check.Refactor.LongQuoteBlocks}, 103 | {Credo.Check.Refactor.MatchInCondition}, 104 | {Credo.Check.Refactor.NegatedConditionsInUnless}, 105 | {Credo.Check.Refactor.NegatedConditionsWithElse}, 106 | {Credo.Check.Refactor.Nesting}, 107 | {Credo.Check.Refactor.PipeChainStart}, 108 | {Credo.Check.Refactor.UnlessWithElse}, 109 | 110 | {Credo.Check.Warning.BoolOperationOnSameValues}, 111 | {Credo.Check.Warning.IExPry}, 112 | {Credo.Check.Warning.IoInspect}, 113 | {Credo.Check.Warning.LazyLogging}, 114 | {Credo.Check.Warning.OperationOnSameValues}, 115 | {Credo.Check.Warning.OperationWithConstantResult}, 116 | {Credo.Check.Warning.UnusedEnumOperation}, 117 | {Credo.Check.Warning.UnusedFileOperation}, 118 | {Credo.Check.Warning.UnusedKeywordOperation}, 119 | {Credo.Check.Warning.UnusedListOperation}, 120 | {Credo.Check.Warning.UnusedPathOperation}, 121 | {Credo.Check.Warning.UnusedRegexOperation}, 122 | {Credo.Check.Warning.UnusedStringOperation}, 123 | {Credo.Check.Warning.UnusedTupleOperation}, 124 | {Credo.Check.Warning.RaiseInsideRescue}, 125 | 126 | # Controversial and experimental checks (opt-in, just remove `, false`) 127 | # 128 | {Credo.Check.Refactor.ABCSize}, 129 | {Credo.Check.Refactor.AppendSingleItem}, 130 | {Credo.Check.Refactor.VariableRebinding}, 131 | {Credo.Check.Warning.MapGetUnsafePass}, 132 | {Credo.Check.Consistency.MultiAliasImportRequireUse}, 133 | 134 | # Deprecated checks (these will be deleted after a grace period) 135 | # 136 | {Credo.Check.Readability.Specs, false}, 137 | {Credo.Check.Warning.NameRedeclarationByAssignment, false}, 138 | {Credo.Check.Warning.NameRedeclarationByCase, false}, 139 | {Credo.Check.Warning.NameRedeclarationByDef, false}, 140 | {Credo.Check.Warning.NameRedeclarationByFn, false}, 141 | 142 | # Custom checks can be created using `mix credo.gen.check`. 143 | # 144 | ] 145 | } 146 | ] 147 | } 148 | -------------------------------------------------------------------------------- /lib/arangoex/user.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.User do 2 | @moduledoc """ 3 | This module contains functions used to manage users. 4 | """ 5 | 6 | @doc """ 7 | Create a new user. 8 | 9 | The `conn` parameter is an ArangoDB connection PID. The `user` parameter is a map describing the user to be created. 10 | 11 | ## Endpoint 12 | 13 | POST /_api/user 14 | 15 | ## Options 16 | 17 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 18 | 19 | ## Examples 20 | 21 | {:ok, conn} = Arangoex.start_link() 22 | {:ok, resp} = Arangoex.User.create(conn, %{user: "foo"}) 23 | """ 24 | def create(conn, %{} = user, opts \\ []) do 25 | Arangoex.request(conn, :post, "/_api/user", %{}, %{}, user, opts) 26 | end 27 | 28 | @doc """ 29 | Return information about the databases available to a user. 30 | 31 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is a string name of the user whose 32 | database information should be returned. 33 | 34 | ## Endpoint 35 | 36 | GET /_api/user/{user_name}/database 37 | 38 | ## Options 39 | 40 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 41 | 42 | ## Examples 43 | 44 | {:ok, conn} = Arangoex.start_link() 45 | {:ok, resp} = Arangoex.User.database(conn, "foo") 46 | """ 47 | def database(conn, user_name, opts \\ []) do 48 | Arangoex.request(conn, :get, "/_api/user/#{user_name}/database", %{}, %{}, nil, opts) 49 | end 50 | 51 | @doc """ 52 | Return information about a user. 53 | 54 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is a string name of the user whose 55 | information should be returned. 56 | 57 | ## Endpoint 58 | 59 | GET /_api/user/{user_name} 60 | 61 | ## Options 62 | 63 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 64 | 65 | ## Examples 66 | 67 | {:ok, conn} = Arangoex.start_link() 68 | {:ok, resp} = Arangoex.User.get(conn, "foo") 69 | """ 70 | def get(conn, user_name, opts \\ []) do 71 | Arangoex.request(conn, :get, "/_api/user/#{user_name}", %{}, %{}, nil, opts) 72 | end 73 | 74 | @doc """ 75 | Grant database access to a user. 76 | 77 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is a string name of the user who will 78 | be granted database access. The `database_name` parameter is the string name of the database to which the user will 79 | be granted access. 80 | 81 | ## Endpoint 82 | 83 | PUT /_api/user/{user}/database/{dbname} 84 | 85 | ## Options 86 | 87 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 88 | 89 | ## Examples 90 | 91 | {:ok, conn} = Arangoex.start_link() 92 | {:ok, resp} = Arangoex.User.grant(conn, "foo", "bar") 93 | """ 94 | def grant(conn, user_name, database_name, opts \\ []) do 95 | Arangoex.request(conn, :put, "/_api/user/#{user_name}/database/#{database_name}", %{}, %{}, %{grant: "rw"}, opts) 96 | end 97 | 98 | @doc """ 99 | Return information about all users the current user has access to. 100 | 101 | The `conn` parameter is an ArangoDB connection PID. 102 | 103 | ## Endpoint 104 | 105 | GET /_api/user 106 | 107 | ## Options 108 | 109 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 110 | 111 | ## Examples 112 | 113 | {:ok, conn} = Arangoex.start_link() 114 | {:ok, resp} = Arangoex.User.list(conn) 115 | """ 116 | def list(conn, opts \\ []) do 117 | Arangoex.request(conn, :get, "/_api/user", %{}, %{}, nil, opts) 118 | end 119 | 120 | @doc """ 121 | Remove a user from the system. 122 | 123 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is a string name of the user to be 124 | removed. 125 | 126 | ## Endpoint 127 | 128 | DELETE /_api/user/{user_name} 129 | 130 | ## Options 131 | 132 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 133 | 134 | ## Examples 135 | 136 | {:ok, conn} = Arangoex.start_link() 137 | {:ok, resp} = Arangoex.User.remove(conn, "foo") 138 | """ 139 | def remove(conn, user_name, opts \\ []) do 140 | Arangoex.request(conn, :delete, "/_api/user/#{user_name}", %{}, %{}, nil, opts) 141 | end 142 | 143 | @doc """ 144 | Replace the properties of a user. 145 | 146 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is the string name of the user whose 147 | properties should be replaced. The `user` parameter is a map describing the replacement user properties. 148 | 149 | ## Endpoint 150 | 151 | PUT /_api/user/{user_name} 152 | 153 | ## Options 154 | 155 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 156 | 157 | ## Examples 158 | 159 | {:ok, conn} = Arangoex.start_link() 160 | {:ok, resp} = Arangoex.User.replace(conn, "foo", %{password: "bar") 161 | """ 162 | def replace(conn, user_name, %{} = user, opts \\ []) do 163 | Arangoex.request(conn, :put, "/_api/user/#{user_name}", %{}, %{}, user, opts) 164 | end 165 | 166 | @doc """ 167 | Revoke database access from a user. 168 | 169 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is a string name of the user whose 170 | access will be revoked. The `database_name` parameter is the string name of the database to which the user's access 171 | will be revoked. 172 | 173 | ## Endpoint 174 | 175 | PUT /_api/user/{user}/database/{dbname} 176 | 177 | ## Options 178 | 179 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 180 | 181 | ## Examples 182 | 183 | {:ok, conn} = Arangoex.start_link() 184 | {:ok, resp} = Arangoex.User.revoke(conn, "foo", "bar") 185 | """ 186 | def revoke(conn, user_name, database_name, opts \\ []) do 187 | Arangoex.request(conn, :put, "/_api/user/#{user_name}/database/#{database_name}", %{}, %{}, %{grant: "none"}, opts) 188 | end 189 | 190 | @doc """ 191 | Update the properties of a user. 192 | 193 | The `conn` parameter is an ArangoDB connection PID. The `user_name` parameter is the string name of the user whose 194 | properties should be updated. The `user` parameter is a map describing the updated user properties. 195 | 196 | ## Endpoint 197 | 198 | PATCH /_api/user/{user_name} 199 | 200 | ## Options 201 | 202 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 203 | 204 | ## Examples 205 | 206 | {:ok, conn} = Arangoex.start_link() 207 | {:ok, resp} = Arangoex.User.update(conn, "foo", %{password: "bar") 208 | """ 209 | def update(conn, user_name, %{} = user, opts \\ []) do 210 | Arangoex.request(conn, :patch, "/_api/user/#{user_name}", %{}, %{}, user, opts) 211 | end 212 | end 213 | -------------------------------------------------------------------------------- /test/arangoex/collection_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.CollectionTest do 2 | alias Arangoex.Collection 3 | 4 | use ExUnit.Case, async: false 5 | 6 | setup do 7 | # make sure "foo" collection does not exist for tests 8 | Collection.remove(:arango, "foo") 9 | 10 | on_exit fn -> 11 | # make sure "foo" collection is removed from any test 12 | Collection.remove(:arango, "foo") 13 | end 14 | end 15 | 16 | test "count() returns the number of documents in a collection" do 17 | # todo - create collection, create documents, count documents 18 | end 19 | 20 | test "create() creates a new collection." do 21 | {:ok, resp} = Collection.create(:arango, %{name: "foo"}) 22 | 23 | assert resp.status_code == 200 24 | assert resp.body["code"] == 200 25 | assert resp.body["error"] === false 26 | assert is_binary(resp.body["id"]) 27 | assert resp.body["isSystem"] === false 28 | assert resp.body["isVolatile"] === false 29 | assert resp.body["name"] === "foo" 30 | assert resp.body["status"] === 3 # todo - what does this mean? 31 | assert resp.body["type"] === 2 # todo - what does this mean? 32 | assert resp.body["waitForSync"] === false 33 | 34 | foo = %{"id" => resp.body["id"], "isSystem" => false, "name" => "foo", "status" => 3, "type" => 2} 35 | {:ok, list_resp} = Collection.list(:arango) 36 | 37 | assert Enum.member?(list_resp.body["result"], foo) 38 | end 39 | 40 | test "get() returns information about a collection" do 41 | Collection.create(:arango, %{name: "foo"}) 42 | {:ok, resp} = Collection.get(:arango, "foo") 43 | 44 | assert resp.status_code == 200 45 | assert resp.body["code"] == 200 46 | assert resp.body["error"] === false 47 | assert is_binary(resp.body["id"]) 48 | assert resp.body["isSystem"] === false 49 | assert resp.body["name"] === "foo" 50 | assert resp.body["status"] === 3 # todo - what does this mean? 51 | assert resp.body["type"] === 2 # todo - what does this mean? 52 | end 53 | 54 | test "checksum() returns the checksum for a collection" do 55 | Collection.create(:arango, %{name: "foo"}) 56 | {:ok, resp} = Collection.checksum(:arango, "foo") 57 | 58 | assert resp.status_code == 200 59 | assert resp.body["code"] == 200 60 | assert resp.body["error"] === false 61 | assert is_binary(resp.body["id"]) 62 | assert resp.body["isSystem"] === false 63 | assert resp.body["checksum"] === "0" # todo - what does this mean? 64 | assert resp.body["name"] === "foo" 65 | assert resp.body["status"] === 3 # todo - what does this mean? 66 | assert resp.body["type"] === 2 # todo - what does this mean? 67 | assert resp.body["revision"] === "0" # todo - what does this mean? 68 | end 69 | 70 | test "get_properties() returns the propeties of a collection" do 71 | Collection.create(:arango, %{name: "foo"}) 72 | {:ok, resp} = Collection.get_properties(:arango, "foo") 73 | 74 | assert resp.status_code == 200 75 | assert resp.body["code"] == 200 76 | assert resp.body["error"] === false 77 | assert is_binary(resp.body["id"]) 78 | assert is_integer(resp.body["journalSize"]) 79 | assert resp.body["isSystem"] === false 80 | assert resp.body["isVolatile"] === false 81 | assert resp.body["doCompact"] === true 82 | assert resp.body["name"] === "foo" 83 | assert resp.body["status"] === 3 # todo - what does this mean? 84 | assert resp.body["type"] === 2 # todo - what does this mean? 85 | assert resp.body["waitForSync"] === false 86 | assert resp.body["keyOptions"] === %{"allowUserKeys" => true, "lastValue" => 0, "type" => "traditional"} # todo - what does this mean? 87 | end 88 | 89 | test "list() returns information about databases in the system." do 90 | {:ok, coll} = Collection.create(:arango, %{name: "foo"}) 91 | {:ok, resp} = Collection.list(:arango) 92 | 93 | assert resp.status_code == 200 94 | assert resp.body["code"] == 200 95 | assert resp.body["error"] === false 96 | assert is_list(resp.body["result"]) 97 | 98 | foo = %{"id" => coll.body["id"], "isSystem" => false, "name" => "foo", "status" => 3, "type" => 2} 99 | assert Enum.member?(resp.body["result"], foo) 100 | end 101 | 102 | test "load() loads a collection from memory" do 103 | Collection.create(:arango, %{name: "foo"}) 104 | {:ok, resp} = Collection.load(:arango, "foo") 105 | 106 | assert resp.status_code == 200 107 | assert resp.body["code"] == 200 108 | assert resp.body["error"] === false 109 | assert is_binary(resp.body["id"]) 110 | assert resp.body["count"] === 0 111 | assert resp.body["isSystem"] === false 112 | assert resp.body["name"] === "foo" 113 | assert resp.body["status"] === 3 # todo - what does this mean? 114 | assert resp.body["type"] === 2 # todo - what does this mean? 115 | end 116 | 117 | test "remove() deletes the given collection." do 118 | Collection.create(:arango, %{name: "foo"}) 119 | {:ok, resp} = Collection.remove(:arango, "foo") 120 | 121 | assert resp.status_code == 200 122 | assert resp.body["code"] == 200 123 | assert resp.body["error"] === false 124 | assert is_binary(resp.body["id"]) 125 | 126 | foo = %{"id" => resp.body["id"], "isSystem" => false, "name" => "foo", "status" => 3, "type" => 2} 127 | {:ok, list_resp} = Collection.list(:arango) 128 | 129 | refute Enum.member?(list_resp.body["result"], foo) 130 | end 131 | 132 | test "revision() returns the revision of the collection." do 133 | Collection.create(:arango, %{name: "foo"}) 134 | {:ok, resp} = Collection.revision(:arango, "foo") 135 | 136 | assert resp.status_code == 200 137 | assert resp.body["code"] == 200 138 | assert resp.body["error"] === false 139 | assert is_binary(resp.body["id"]) 140 | assert resp.body["revision"] === "0" 141 | assert resp.body["isSystem"] === false 142 | assert resp.body["name"] === "foo" 143 | assert resp.body["status"] === 3 # todo - what does this mean? 144 | assert resp.body["type"] === 2 # todo - what does this mean? 145 | end 146 | 147 | test "rotate() rotates the journal of a collection." do 148 | Collection.create(:arango, %{name: "foo"}) 149 | # {:ok, resp} = Collection.rotate(:arango, "foo") 150 | 151 | # todo 152 | # assert resp.status_code == 200 153 | # assert resp.body["code"] == 200 154 | # assert resp.body["error"] === false 155 | # assert is_binary(resp.body["id"]) 156 | end 157 | 158 | test "statistics() returns statistics about a a collection." do 159 | Collection.create(:arango, %{name: "foo"}) 160 | # {:ok, resp} = Collection.statistics(:arango, "foo") 161 | 162 | # todo 163 | # assert resp.status_code == 200 164 | # assert resp.body["code"] == 200 165 | # assert resp.body["error"] === false 166 | # assert is_binary(resp.body["id"]) 167 | end 168 | 169 | test "truncate() removes all documects from a collection, leaving indexes intact." do 170 | # todo 171 | end 172 | 173 | test "unload() unloads a collection from memory" do 174 | Collection.create(:arango, %{name: "foo"}) 175 | Collection.load(:arango, "foo") 176 | {:ok, resp} = Collection.unload(:arango, "foo") 177 | 178 | assert resp.status_code == 200 179 | assert resp.body["code"] == 200 180 | assert resp.body["error"] === false 181 | assert is_binary(resp.body["id"]) 182 | assert resp.body["isSystem"] === false 183 | assert resp.body["name"] === "foo" 184 | # assert resp.body["status"] === ? # todo - what does this mean? 2 or 4? 185 | assert resp.body["type"] === 2 # todo - what does this mean? 186 | end 187 | end 188 | -------------------------------------------------------------------------------- /lib/arangoex/collection.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Collection do 2 | @moduledoc """ 3 | This module contains functions used to manage collections. 4 | """ 5 | 6 | @doc """ 7 | Return number of documents in the collection. 8 | 9 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 10 | collection whose count should be returned. 11 | 12 | ## Endpoint 13 | 14 | PUT /_api/collection/{collection_name}/count 15 | 16 | ## Options 17 | 18 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 19 | 20 | ## Examples 21 | 22 | {:ok, conn} = Arangoex.start_link() 23 | {:ok, resp} = Arangoex.Collection.count(conn, "foo") 24 | """ 25 | def count(conn, collection_name, opts \\ []) do 26 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}/count", %{}, %{}, nil, opts) 27 | end 28 | 29 | @doc """ 30 | Create a new collection. 31 | 32 | The `conn` parameter is an ArangoDB connection PID. The `collection` parameter is a map describing the collection to 33 | be created. 34 | 35 | ## Endpoint 36 | 37 | POST /_api/collection 38 | 39 | ## Options 40 | 41 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 42 | 43 | ## Examples 44 | 45 | {:ok, conn} = Arangoex.start_link() 46 | {:ok, resp} = Arangoex.Collection.create(conn, %{name: "foo"}) 47 | """ 48 | def create(conn, %{} = collection, opts \\ []) do 49 | # todo - waitforsynch and all sorts of options? 50 | Arangoex.request(conn, :post, "/_api/collection", %{}, %{}, collection, opts) 51 | end 52 | 53 | @doc """ 54 | Return information about a collections. 55 | 56 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 57 | collection whose information should be returned. 58 | 59 | ## Endpoint 60 | 61 | GET /_api/collection/{collection_name} 62 | 63 | ## Options 64 | 65 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 66 | 67 | ## Examples 68 | 69 | {:ok, conn} = Arangoex.start_link() 70 | {:ok, resp} = Arangoex.Collection.get(conn, "foo") 71 | """ 72 | def get(conn, collection_name, opts \\ []) do 73 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}", %{}, %{}, nil, opts) 74 | end 75 | 76 | @doc """ 77 | Return the checksum for a collection. 78 | 79 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 80 | collection whose checksum should be returned. 81 | 82 | ## Endpoint 83 | 84 | PUT /_api/collection/{collection_name}/checksum 85 | 86 | ## Options 87 | 88 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 89 | 90 | ## Examples 91 | 92 | {:ok, conn} = Arangoex.start_link() 93 | {:ok, resp} = Arangoex.Collection.checksum(conn, "foo") 94 | """ 95 | def checksum(conn, collection_name, opts \\ []) do 96 | # todo - implement withRevisions and withData query parameters 97 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}/checksum", %{}, %{}, nil, opts) 98 | end 99 | 100 | @doc """ 101 | Return the properties of a collection. 102 | 103 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 104 | collection whose properties should be returned. 105 | 106 | ## Endpoint 107 | 108 | PUT /_api/collection/{collection_name}/properties 109 | 110 | ## Options 111 | 112 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 113 | 114 | ## Examples 115 | 116 | {:ok, conn} = Arangoex.start_link() 117 | {:ok, resp} = Arangoex.Collection.properties(conn, "foo") 118 | """ 119 | def get_properties(conn, collection_name, opts \\ []) do 120 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}/properties", %{}, %{}, nil, opts) 121 | end 122 | 123 | @doc """ 124 | Return information about all collections in the database 125 | 126 | The `conn` parameter is an ArangoDB connection PID. 127 | 128 | ## Endpoint 129 | 130 | GET /_api/collection 131 | 132 | ## Options 133 | 134 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 135 | 136 | ## Examples 137 | 138 | {:ok, conn} = Arangoex.start_link() 139 | {:ok, resp} = Arangoex.Collection.list(conn) 140 | """ 141 | def list(conn, opts \\ []) do 142 | # todo - implement excludeSystem query parameter 143 | Arangoex.request(conn, :get, "/_api/collection", %{}, %{}, nil, opts) 144 | end 145 | 146 | @doc """ 147 | Load a collection into memory. 148 | 149 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 150 | collection to be loaded. 151 | 152 | ## Endpoint 153 | 154 | PUT /_api/collection/{collection_name}/load 155 | 156 | ## Options 157 | 158 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 159 | 160 | ## Examples 161 | 162 | {:ok, conn} = Arangoex.start_link() 163 | {:ok, resp} = Arangoex.Collection.load(conn, "foo") 164 | """ 165 | def load(conn, collection_name, opts \\ []) do 166 | Arangoex.request(conn, :put, "/_api/collection/#{collection_name}/load", %{}, %{}, nil, opts) 167 | end 168 | 169 | @doc """ 170 | Remove a collection. 171 | 172 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 173 | collection to be removed. 174 | 175 | ## Endpoint 176 | 177 | DELETE /_api/collection/{collection_name} 178 | 179 | ## Options 180 | 181 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 182 | 183 | ## Examples 184 | 185 | {:ok, conn} = Arangoex.start_link() 186 | {:ok, resp} = Arangoex.Collection.remove(conn, "foo") 187 | """ 188 | def remove(conn, collection_name, opts \\ []) do 189 | # todo - implement isSystem query parameter 190 | Arangoex.request(conn, :delete, "/_api/collection/#{collection_name}", %{}, %{}, nil, opts) 191 | end 192 | 193 | # PUT /_api/collection/{collection-name}/rename 194 | # Rename the collection identified by collection-name. 195 | def rename(collection_name, collection, opts \\ []) do 196 | # todo 197 | # {:ok, body} = JSON.encode(collection) 198 | # 199 | # [collection_name, "/", "rename"] 200 | # |> build_url(opts) 201 | # |> Arangoex.put(body, opts) 202 | end 203 | 204 | @doc """ 205 | Return the revision id of a collection. 206 | 207 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 208 | collection from which to fetch the revision id. 209 | 210 | ## Endpoint 211 | 212 | GET /_api/collection/{collection_name}/revision 213 | 214 | ## Options 215 | 216 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 217 | 218 | ## Examples 219 | 220 | {:ok, conn} = Arangoex.start_link() 221 | {:ok, resp} = Arangoex.Collection.revision(conn, "foo") 222 | """ 223 | def revision(conn, collection_name, opts \\ []) do 224 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}/revision", %{}, %{}, nil, opts) 225 | end 226 | 227 | @doc """ 228 | Rotate the journal of a collection 229 | 230 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 231 | collection to be rotated. 232 | 233 | ## Endpoint 234 | 235 | PUT /_api/collection/{collection_name}/rotate 236 | 237 | ## Options 238 | 239 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 240 | 241 | ## Examples 242 | 243 | {:ok, conn} = Arangoex.start_link() 244 | {:ok, resp} = Arangoex.Collection.rotate(conn, "foo") 245 | """ 246 | def rotate(conn, collection_name, opts \\ []) do 247 | Arangoex.request(conn, :put, "/_api/collection/#{collection_name}/rotate", %{}, %{}, nil, opts) 248 | end 249 | 250 | # PUT /_api/collection/{collection-name}/properties 251 | # Change the properties of the collection identified by collection-name. 252 | def set_properties(collection_name, properties, opts \\ []) do 253 | # todo 254 | # {:ok, body} = JSON.encode(properties) 255 | # 256 | # [collection_name, "/", "properties"] 257 | # |> build_url(opts) 258 | # |> Arangoex.put(body, opts) 259 | end 260 | 261 | @doc """ 262 | Return statistics about a collection. 263 | 264 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 265 | collection whose statistics should be returned. 266 | 267 | ## Endpoint 268 | 269 | PUT /_api/collection/{collection_name}/figures 270 | 271 | ## Options 272 | 273 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 274 | 275 | ## Examples 276 | 277 | {:ok, conn} = Arangoex.start_link() 278 | {:ok, resp} = Arangoex.Collection.figures(conn, "foo") 279 | """ 280 | def statistics(conn, collection_name, opts \\ []) do 281 | Arangoex.request(conn, :get, "/_api/collection/#{collection_name}/figures", %{}, %{}, nil, opts) 282 | end 283 | 284 | @doc """ 285 | Remove all documents from a collection, leaving indexes intact. 286 | 287 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 288 | collection to be truncated. 289 | 290 | ## Endpoint 291 | 292 | PUT /_api/collection/{collection_name}/truncate 293 | 294 | ## Options 295 | 296 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 297 | 298 | ## Examples 299 | 300 | {:ok, conn} = Arangoex.start_link() 301 | {:ok, resp} = Arangoex.Collection.truncate(conn, "foo") 302 | """ 303 | def truncate(conn, collection_name, opts \\ []) do 304 | Arangoex.request(conn, :put, "/_api/collection/#{collection_name}/truncate", %{}, %{}, nil, opts) 305 | end 306 | 307 | @doc """ 308 | Unload a collection from memory. 309 | 310 | The `conn` parameter is an ArangoDB connection PID. The `collection_name` parameter is a string name of the 311 | collection to be unloaded. 312 | 313 | ## Endpoint 314 | 315 | PUT /_api/collection/{collection_name}/unload 316 | 317 | ## Options 318 | 319 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 320 | 321 | ## Examples 322 | 323 | {:ok, conn} = Arangoex.start_link() 324 | {:ok, resp} = Arangoex.Collection.unload(conn, "foo") 325 | """ 326 | def unload(conn, collection_name, opts \\ []) do 327 | Arangoex.request(conn, :put, "/_api/collection/#{collection_name}/unload", %{}, %{}, nil, opts) 328 | end 329 | end 330 | -------------------------------------------------------------------------------- /lib/arangoex/simple.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Simple do 2 | @moduledoc """ 3 | ArangoDB HTTP Interface for Simple Queries 4 | 5 | ## TODO 6 | 7 | * Implement function for `PUT /_api/simple/within-rectangle` 8 | * Specify typespecs 9 | 10 | """ 11 | 12 | # use Arangoex, base_url: ["/", "_api", "/", "simple"] 13 | # alias Arangoex.{JSON,Response} 14 | 15 | @doc """ 16 | Returns the first document in the collection identified by 17 | `collection_name` that matches the example document. 18 | 19 | The HTTP requests looks like: 20 | 21 | PUT /_api/simple/first-example 22 | 23 | ## Arguments 24 | 25 | * `collection_name` - a string representing the collection name 26 | * `example` - a map representing an example for the query to use 27 | * `opts` - a keyword list of options on how to make the query 28 | 29 | ## Options 30 | 31 | * `query_params` - a keyword list of query params to build into the 32 | request url 33 | * `headers` - a list of 2-length tuples for HTTP headers 34 | (e.g. [{"Connection", "close"}] used in request 35 | 36 | ## Example 37 | 38 | iex> Arangoex.Simple.get_first_document_by_example("exotic_cars", %{brand: "lamborghini"}) 39 | 40 | """ 41 | @spec get_first_document_by_example(binary, map, keyword) :: {:ok, Response.t} 42 | def get_first_document_by_example(collection_name, example, opts \\ []) do 43 | # {:ok, body} = JSON.encode(%{collection: collection_name, example: example}) 44 | # 45 | # "first-example" 46 | # |> build_url(opts) 47 | # |> Arangoex.put(body, opts) 48 | end 49 | 50 | @doc """ 51 | Return a random document from the collection identified by `collection_name`. 52 | 53 | The HTTP requests looks like: 54 | 55 | PUT /_api/simple/any 56 | 57 | ## Arguments 58 | 59 | * `collection_name` - a string representing the collection name 60 | * `opts` - a keyword list of options on how to make the query 61 | 62 | ## Options 63 | 64 | * `query_params` - a keyword list of query params to build into the 65 | request url 66 | * `headers` - a list of 2-length tuples for HTTP headers 67 | (e.g. [{"Connection", "close"}] used in request 68 | 69 | ## Example 70 | 71 | iex> Arangoex.Simple.get_random_document("exotic_cars") 72 | 73 | """ 74 | def get_random_document(collection_name, opts \\ []) do 75 | # {:ok, body} = JSON.encode(%{collection: collection_name}) 76 | # 77 | # "any" 78 | # |> build_url(opts) 79 | # |> Arangoex.put(body, opts) 80 | end 81 | 82 | @doc """ 83 | Returns all documents in a collection. 84 | 85 | The HTTP requests looks like: 86 | 87 | PUT /_api/simple/all 88 | 89 | ## Arguments 90 | 91 | * `collection_name` - a string representing the collection name 92 | * `attrs` - a keyword list of attributes specific to this type of query 93 | * `opts` - a keyword list of options on how to make the query 94 | 95 | ## Attributes (optional) 96 | 97 | * `skip` - the number of documents to skip in the query 98 | * `limit` - the maximum number of documents to return in the query 99 | 100 | ## Options 101 | 102 | * `query_params` - a keyword list of query params to build into the 103 | request url 104 | * `headers` - a list of 2-length tuples for HTTP headers 105 | (e.g. [{"Connection", "close"}] used in request 106 | 107 | ## Example 108 | 109 | iex> Arangoex.Simple.list_documents("exotic_cars") 110 | 111 | """ 112 | def list_documents(collection_name, attrs \\ [], opts \\ []) do 113 | # {:ok, body} = attrs 114 | # |> Enum.into(%{collection: collection_name}) 115 | # |> JSON.encode 116 | # 117 | # "all" 118 | # |> build_url(opts) 119 | # |> Arangoex.put(body, opts) 120 | end 121 | 122 | @doc """ 123 | Return all documents in the collection identified by `collection_name` 124 | that match the example document. 125 | 126 | The HTTP requests looks like: 127 | 128 | PUT /_api/simple/by-example 129 | 130 | ## Arguments 131 | 132 | * `collection_name` - a string representing the collection name 133 | * `example` - a map representing the structure of an example document 134 | * `attrs` - a keyword list of attributes specific to this type of query 135 | * `opts` - a keyword list of options on how to make the query 136 | 137 | ## Attributes (optional) 138 | 139 | * `skip` - the number of documents to skip in the query 140 | * `limit` - the maximum number of documents to return in the query 141 | * `batchSize` - the maximum number of results returned in single query 142 | 143 | ## Options 144 | 145 | * `query_params` - a keyword list of query params to build into the 146 | request url 147 | * `headers` - a list of 2-length tuples for HTTP headers 148 | (e.g. [{"Connection", "close"}] used in request 149 | 150 | ## Example 151 | 152 | iex> Arangoex.Simple.list_documents_by_example("exotic_cars", %{continent: "european"}) 153 | 154 | """ 155 | def list_documents_by_example(collection_name, example, attrs \\ [], opts \\ []) do 156 | # {:ok, body} = attrs 157 | # |> Enum.into(%{collection: collection_name, example: example}) 158 | # |> JSON.encode 159 | # 160 | # "by-example" 161 | # |> build_url(opts) 162 | # |> Arangoex.put(body, opts) 163 | end 164 | 165 | @doc """ 166 | Return all documents in the collection identified by `collection_name` 167 | for the given keys. 168 | 169 | The HTTP requests looks like: 170 | 171 | PUT /_api/simple/lookup-by-keys 172 | 173 | ## Arguments 174 | 175 | * `collection_name` - a string representing the collection name 176 | * `keys` - a list of keys of documents to retrieve 177 | * `opts` - a keyword list of options on how to make the query 178 | 179 | ## Options 180 | 181 | * `query_params` - a keyword list of query params to build into the 182 | request url 183 | * `headers` - a list of 2-length tuples for HTTP headers 184 | (e.g. [{"Connection", "close"}] used in request 185 | 186 | ## Example 187 | 188 | iex> Arangoex.Simple.list_documents_for_keys("exotic_cars", ["car1", "car2"]}) 189 | 190 | """ 191 | def list_documents_for_keys(collection_name, keys \\ [], opts \\ []) do 192 | # {:ok, body} = JSON.encode(%{collection: collection_name, keys: keys}) 193 | # 194 | # "lookup-by-keys" 195 | # |> build_url(opts) 196 | # |> Arangoex.put(body, opts) 197 | end 198 | 199 | @doc """ 200 | Returns a list of keys/ids/paths for documents in a collection. 201 | 202 | The HTTP requests looks like: 203 | 204 | PUT /_api/simple/all-keys 205 | 206 | ## Arguments 207 | 208 | * `collection_name` - a string representing the collection name 209 | * `type` - a string representing the type of result (defaults to `path`) 210 | * `opts` - a keyword list of options on how to make the query 211 | 212 | ## Supported Types 213 | 214 | * `id` - returns a list of document ids (`_id` attributes) 215 | * `key` - returns a list of document keys (`_key` attributes) 216 | * `path` - returns a list of document URI paths (`_path` attributes) 217 | 218 | ## Options 219 | 220 | * `query_params` - a keyword list of query params to build into the 221 | request url 222 | * `headers` - a list of 2-length tuples for HTTP headers 223 | (e.g. [{"Connection", "close"}] used in request 224 | 225 | ## Example 226 | 227 | iex> Arangoex.Simple.list_keys("exotic_cars", "key") 228 | 229 | """ 230 | def list_keys(collection, type \\ "path", opts \\ []) do 231 | # {:ok, body} = JSON.encode(%{collection: collection, type: type}) 232 | # 233 | # "all-keys" 234 | # |> build_url(opts) 235 | # |> Arangoex.put(body, opts) 236 | end 237 | 238 | @doc """ 239 | Remove documents in the collection identified by `collection_name` 240 | that match the example document. 241 | 242 | The HTTP requests looks like: 243 | 244 | PUT /_api/simple/remove-by-example 245 | 246 | ## Arguments 247 | 248 | * `collection_name` - a string representing the collection name 249 | * `example` - a document to compare documents against for matching 250 | * `attrs` - a keyword list of attributes specific to this type of query 251 | * `opts` - a keyword list of options on how to make the query 252 | 253 | ## Attributes 254 | 255 | * `limit` - the maximum number of documents to delete in the query 256 | * `waitForSync` - if `true`, remove operations will be instantly 257 | synced to disk, otherwise normal sync behavior will occur 258 | 259 | ## Options 260 | 261 | * `query_params` - a keyword list of query params to build into the 262 | request url 263 | * `headers` - a list of 2-length tuples for HTTP headers 264 | (e.g. [{"Connection", "close"}] used in request 265 | 266 | ## Example 267 | 268 | iex> Arangoex.Simple.remove_documents_by_example("movies", %{category: "comedy"}]) 269 | 270 | """ 271 | def remove_documents_by_example(collection_name, example, attrs \\ [], opts \\ []) do 272 | # {:ok, body} = attrs 273 | # |> Enum.into(%{collection: collection_name, example: example}) 274 | # |> JSON.encode 275 | # 276 | # "remove-by-example" 277 | # |> build_url(opts) 278 | # |> Arangoex.put(body, opts) 279 | end 280 | 281 | @doc """ 282 | Remove all documents in the collection identified by `collection_name` 283 | for the given keys. 284 | 285 | The HTTP requests looks like: 286 | 287 | PUT /_api/simple/remove-by-keys 288 | 289 | ## Arguments 290 | 291 | * `collection_name` - a string representing the collection name 292 | * `keys` - a list of the `_keys` of documents to remove 293 | * `attrs` - a keyword list of attributes specific to this type of query 294 | * `opts` - a keyword list of options on how to make the query 295 | 296 | ## Attributes 297 | 298 | * `returnOld` - if `true` and `silent` is `false`, then it returns 299 | the complete documents that were removed 300 | * `silent` - if `false`, then returns list with removed documents 301 | (default: returns `_id`, `_key`, `_rev`) 302 | * `waitForSync` - if `true`, remove operations will be instantly 303 | synced to disk, otherwise normal sync behavior will occur 304 | 305 | ## Options 306 | 307 | * `query_params` - a keyword list of query params to build into the 308 | request url 309 | * `headers` - a list of 2-length tuples for HTTP headers 310 | (e.g. [{"Connection", "close"}] used in request 311 | 312 | ## Example 313 | 314 | iex> Arangoex.Simple.remove_documents_for_keys("movies", ["movie1", "movie2"]) 315 | 316 | """ 317 | def remove_documents_for_keys(collection_name, keys \\ [], attrs \\ [], opts \\ []) do 318 | # {:ok, body} = attrs 319 | # |> Enum.into(%{collection: collection_name, keys: keys}) 320 | # |> JSON.encode 321 | # 322 | # "remove-by-keys" 323 | # |> build_url(opts) 324 | # |> Arangoex.put(body, opts) 325 | end 326 | 327 | @doc """ 328 | Replace all documents in the collection identified by 329 | `collection_name` that match the example document. 330 | 331 | The HTTP requests looks like: 332 | 333 | PUT /_api/simple/replace-by-example 334 | 335 | ## Arguments 336 | 337 | * `collection_name` - a string representing the collection name 338 | * `example` - a document to compare documents against for matching 339 | * `document` - a document to replace the matched documents with 340 | * `attrs` - a keyword list of attributes specific to this type of query 341 | * `opts` - a keyword list of options on how to make the query 342 | 343 | ## Attributes 344 | 345 | * `limit` - the maximum number of documents to delete in the query 346 | * `waitForSync` - if `true`, remove operations will be instantly 347 | synced to disk, otherwise normal sync behavior will occur 348 | 349 | ## Options 350 | 351 | * `query_params` - a keyword list of query params to build into the 352 | request url 353 | * `headers` - a list of 2-length tuples for HTTP headers 354 | (e.g. [{"Connection", "close"}] used in request 355 | 356 | ## Example 357 | 358 | iex> Arangoex.Simple.replace_documents_by_example("soda-inventory", %{brand: "coke"}, %{brand: "pepsi"}) 359 | 360 | """ 361 | def replace_documents_by_example(collection_name, example, document, attrs \\ [], opts \\ []) do 362 | # {:ok, body} = attrs 363 | # |> Enum.into(%{collection: collection_name, example: example, newValue: document}) 364 | # |> JSON.encode 365 | # 366 | # "replace-by-example" 367 | # |> build_url(opts) 368 | # |> Arangoex.put(body, opts) 369 | end 370 | 371 | @doc """ 372 | Update all documents in the collection identified by 373 | `collection_name` that match the example document. 374 | 375 | The HTTP requests looks like: 376 | 377 | PUT /_api/simple/update-by-example 378 | 379 | ## Arguments 380 | 381 | * `collection_name` - a string representing the collection name 382 | * `example` - a document to compare documents against for matching 383 | * `document` - a document to update/patch the matched documents with 384 | * `attrs` - a keyword list of attributes specific to this type of query 385 | * `opts` - a keyword list of options on how to make the query 386 | 387 | ## Attributes 388 | 389 | * `keepNull` - if `false`, all attributes in data with null values 390 | will be removed from updated document. 391 | * `mergeObjects` - if `false`, the value in the patch document will 392 | overwrite the existing value, otherwise, objects will be 393 | merged. (default: true) 394 | * `limit` - the maximum number of documents to delete in the query 395 | * `waitForSync` - if `true`, remove operations will be instantly 396 | synced to disk, otherwise normal sync behavior will occur 397 | 398 | ## Options 399 | 400 | * `query_params` - a keyword list of query params to build into the 401 | request url 402 | * `headers` - a list of 2-length tuples for HTTP headers 403 | (e.g. [{"Connection", "close"}] used in request 404 | 405 | ## Example 406 | 407 | iex> Arangoex.Simple.update_documents_by_example("soda-inventory", %{brand: "coke"}, %{brand: "pepsi"}) 408 | 409 | """ 410 | def update_documents_by_example(collection_name, example, document, attrs \\ [], opts \\ []) do 411 | # {:ok, body} = attrs 412 | # |> Enum.into(%{collection: collection_name, example: example, newValue: document}) 413 | # |> JSON.encode 414 | # 415 | # "update-by-example" 416 | # |> build_url(opts) 417 | # |> Arangoex.put(body, opts) 418 | end 419 | end 420 | -------------------------------------------------------------------------------- /test/arangoex/graph_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.GraphTest do 2 | alias Arangoex.Collection 3 | alias Arangoex.Graph 4 | 5 | use ExUnit.Case, async: false 6 | 7 | setup do 8 | Graph.remove(:arango, "foo") 9 | 10 | on_exit fn -> 11 | Graph.remove(:arango, "foo") 12 | end 13 | end 14 | 15 | test "add_edges() adds an edge definition to a graph" do 16 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo"}) 17 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "bar"}) 18 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "baz"}) 19 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "my_edges", type: 3}) 20 | edge_def = %{collection: "my_edges", from: ["bar"], to: ["baz"]} 21 | returned_edge_def = %{"collection" => "my_edges", "from" => ["bar"], "to" => ["baz"]} 22 | {:ok, resp} = Graph.add_edges(:arango, "foo", edge_def) 23 | 24 | assert resp.status_code == 202 25 | assert Enum.member?(resp.body["graph"]["edgeDefinitions"], returned_edge_def) 26 | 27 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "bar") 28 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "baz") 29 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "my_edges") 30 | end 31 | 32 | test "add_vertices() adds a vertex collection to a graph" do 33 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo"}) 34 | {:ok, resp} = Graph.add_vertices(:arango, "foo", %{collection: "bar"}) 35 | 36 | assert resp.status_code == 202 37 | assert Enum.member?(resp.body["graph"]["orphanCollections"], "bar") 38 | 39 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "bar") 40 | end 41 | 42 | test "create() creates a new graph" do 43 | {:ok, resp} = Graph.create(:arango, %{name: "foo"}) 44 | 45 | assert resp.status_code == 202 46 | assert resp.body["code"] == 202 47 | assert resp.body["error"] == false 48 | assert resp.body["graph"]["name"] == "foo" 49 | assert resp.body["graph"]["edgeDefinitions"] == [] 50 | assert resp.body["graph"]["orphanCollections"] == [] 51 | assert resp.body["graph"]["isSmart"] == false 52 | assert resp.body["graph"]["numberOfShards"] == 0 53 | assert resp.body["graph"]["smartGraphAttribute"] == "" 54 | assert resp.body["graph"]["_id"] == "_graphs/foo" 55 | assert is_binary(resp.body["graph"]["_rev"]) 56 | end 57 | 58 | test "create_edge()" do 59 | create_standard_graph() 60 | {:ok, %{body: %{"vertex" => %{"_id" => id1}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 61 | {:ok, %{body: %{"vertex" => %{"_id" => id2}}}} = Graph.create_vertex(:arango, "foo", "baz", %{my_key: "my_value"}) 62 | {:ok, resp} = Graph.create_edge(:arango, "foo", "my_edges", %{_from: id1, _to: id2, my_key: "my_value"}) 63 | 64 | assert resp.status_code == 202 65 | assert resp.body["code"] == 202 66 | assert resp.body["error"] === false 67 | assert is_binary(resp.body["edge"]["_id"]) 68 | assert is_binary(resp.body["edge"]["_key"]) 69 | assert is_binary(resp.body["edge"]["_rev"]) 70 | 71 | remove_standard_graph() 72 | end 73 | 74 | test "create_vertex()" do 75 | create_standard_graph() 76 | {:ok, resp} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 77 | 78 | assert resp.status_code == 202 79 | assert resp.body["code"] == 202 80 | assert resp.body["error"] === false 81 | assert is_binary(resp.body["vertex"]["_id"]) 82 | assert is_binary(resp.body["vertex"]["_key"]) 83 | assert is_binary(resp.body["vertex"]["_rev"]) 84 | 85 | remove_standard_graph() 86 | end 87 | 88 | test "get() returns information about the given graph" do 89 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo"}) 90 | {:ok, resp} = Graph.get(:arango, "foo") 91 | 92 | assert resp.status_code == 200 93 | assert resp.body["code"] == 200 94 | assert resp.body["error"] === false 95 | assert resp.body["graph"]["name"] == "foo" 96 | assert resp.body["graph"]["edgeDefinitions"] == [] 97 | assert resp.body["graph"]["orphanCollections"] == [] 98 | assert resp.body["graph"]["isSmart"] == false 99 | assert resp.body["graph"]["numberOfShards"] == 1 100 | assert resp.body["graph"]["smartGraphAttribute"] == "" 101 | assert resp.body["graph"]["_id"] == "_graphs/foo" 102 | assert is_binary(resp.body["graph"]["_rev"]) 103 | end 104 | 105 | test "get_edge() returns information about an edge" do 106 | create_standard_graph() 107 | {:ok, %{body: %{"vertex" => %{"_id" => id1}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 108 | {:ok, %{body: %{"vertex" => %{"_id" => id2}}}} = Graph.create_vertex(:arango, "foo", "baz", %{my_key: "my_value"}) 109 | edge = %{_from: id1, _to: id2, my_key: "my_value"} 110 | {:ok, %{body: %{"edge" => %{"_id" => edge_id}}}} = Graph.create_edge(:arango, "foo", "my_edges", edge) 111 | {:ok, resp} = Graph.get_edge(:arango, "foo", edge_id) 112 | 113 | assert resp.status_code == 200 114 | assert resp.body["code"] == 200 115 | assert resp.body["error"] === false 116 | assert resp.body["edge"]["_id"] == edge_id 117 | assert resp.body["edge"]["_from"] == id1 118 | assert resp.body["edge"]["_to"] == id2 119 | assert resp.body["edge"]["my_key"] == "my_value" 120 | assert is_binary(resp.body["edge"]["_key"]) 121 | assert is_binary(resp.body["edge"]["_rev"]) 122 | 123 | remove_standard_graph() 124 | end 125 | 126 | test "get_vertex() returns information about a vertex" do 127 | create_standard_graph() 128 | {:ok, %{body: %{"vertex" => %{"_id" => id}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 129 | {:ok, resp} = Graph.get_vertex(:arango, "foo", id) 130 | 131 | assert resp.status_code == 200 132 | assert resp.body["code"] == 200 133 | assert resp.body["error"] === false 134 | assert resp.body["vertex"]["_id"] == id 135 | assert resp.body["vertex"]["my_key"] == "my_value" 136 | assert is_binary(resp.body["vertex"]["_key"]) 137 | assert is_binary(resp.body["vertex"]["_rev"]) 138 | 139 | remove_standard_graph() 140 | end 141 | 142 | test "list() returns information about all graphs in the database" do 143 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo"}) 144 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "bar"}) 145 | {:ok, resp} = Graph.list(:arango) 146 | 147 | assert resp.status_code == 200 148 | assert resp.body["code"] == 200 149 | assert resp.body["error"] === false 150 | assert length(resp.body["graphs"]) == 2 151 | assert Enum.at(resp.body["graphs"], 0)["_id"] == "_graphs/foo" 152 | assert Enum.at(resp.body["graphs"], 1)["_id"] == "_graphs/bar" 153 | 154 | {:ok, %{status_code: 202}} = Graph.remove(:arango, "foo") 155 | {:ok, %{status_code: 202}} = Graph.remove(:arango, "bar") 156 | end 157 | 158 | test "list_edges() returns information about edge collections in a graph" do 159 | create_standard_graph() 160 | {:ok, resp} = Graph.list_edges(:arango, "foo") 161 | 162 | assert resp.status_code == 200 163 | assert resp.body["code"] == 200 164 | assert resp.body["error"] === false 165 | assert Enum.member?(resp.body["collections"], "my_edges") 166 | 167 | remove_standard_graph() 168 | end 169 | 170 | test "list_vertices() returns information about vertex collections in a graph" do 171 | create_standard_graph() 172 | {:ok, resp} = Graph.list_vertices(:arango, "foo") 173 | 174 | assert resp.status_code == 200 175 | assert resp.body["code"] == 200 176 | assert resp.body["error"] === false 177 | assert Enum.member?(resp.body["collections"], "bar") 178 | assert Enum.member?(resp.body["collections"], "baz") 179 | 180 | remove_standard_graph() 181 | end 182 | 183 | test "remove() deletes the given graph" do 184 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo"}) 185 | {:ok, resp} = Graph.remove(:arango, "foo") 186 | 187 | assert resp.status_code == 202 188 | assert resp.body["code"] == 202 189 | assert resp.body["error"] === false 190 | assert resp.body["removed"] === true 191 | end 192 | 193 | test "remove_edge() removes an edge from a graph" do 194 | create_standard_graph() 195 | {:ok, %{body: %{"vertex" => %{"_id" => id1}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 196 | {:ok, %{body: %{"vertex" => %{"_id" => id2}}}} = Graph.create_vertex(:arango, "foo", "baz", %{my_key: "my_value"}) 197 | edge = %{_from: id1, _to: id2, my_key: "my_value"} 198 | {:ok, %{body: %{"edge" => %{"_id" => edge_id}}}} = Graph.create_edge(:arango, "foo", "my_edges", edge) 199 | {:ok, resp} = Graph.remove_edge(:arango, "foo", edge_id) 200 | 201 | assert resp.status_code == 202 202 | assert resp.body["code"] == 202 203 | assert resp.body["error"] === false 204 | assert resp.body["removed"] === true 205 | 206 | {:ok, edge_resp} = Graph.get_edge(:arango, "foo", edge_id) 207 | 208 | assert edge_resp.status_code == 404 209 | 210 | remove_standard_graph() 211 | end 212 | 213 | test "remove_edges() removes an edge definition from a graph" do 214 | create_standard_graph() 215 | {:ok, resp} = Graph.remove_edges(:arango, "foo", "my_edges") 216 | 217 | assert resp.status_code == 202 218 | assert resp.body["code"] == 202 219 | assert resp.body["error"] == false 220 | assert resp.body["graph"]["name"] == "foo" 221 | assert resp.body["graph"]["edgeDefinitions"] == [] 222 | assert resp.body["graph"]["orphanCollections"] == ["bar", "baz"] 223 | assert resp.body["graph"]["isSmart"] == false 224 | assert resp.body["graph"]["numberOfShards"] == 1 225 | assert resp.body["graph"]["smartGraphAttribute"] == "" 226 | assert resp.body["graph"]["_id"] == "_graphs/foo" 227 | assert is_binary(resp.body["graph"]["_rev"]) 228 | 229 | {:ok, edge_resp} = Graph.list_edges(:arango, "foo") 230 | 231 | assert edge_resp.status_code == 200 232 | assert edge_resp.body["collections"] === [] 233 | 234 | remove_standard_graph() 235 | end 236 | 237 | test "remove_vertex() removes an edge from a graph" do 238 | create_standard_graph() 239 | {:ok, %{body: %{"vertex" => %{"_id" => id}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 240 | {:ok, resp} = Graph.remove_vertex(:arango, "foo", id) 241 | 242 | assert resp.status_code == 202 243 | assert resp.body["code"] == 202 244 | assert resp.body["error"] === false 245 | assert resp.body["removed"] === true 246 | 247 | {:ok, vertex_resp} = Graph.get_vertex(:arango, "foo", id) 248 | 249 | assert vertex_resp.status_code == 404 250 | 251 | remove_standard_graph() 252 | end 253 | 254 | test "remove_vertices() removes an edge definition from a graph" do 255 | create_standard_graph() 256 | {:ok, %{status_code: 202}} = Graph.remove_edges(:arango, "foo", "my_edges") 257 | {:ok, resp} = Graph.remove_vertices(:arango, "foo", "bar") 258 | 259 | assert resp.status_code == 202 260 | assert resp.body["code"] == 202 261 | assert resp.body["error"] == false 262 | assert resp.body["graph"]["name"] == "foo" 263 | assert resp.body["graph"]["edgeDefinitions"] == [] 264 | assert resp.body["graph"]["orphanCollections"] == ["baz"] 265 | assert resp.body["graph"]["isSmart"] == false 266 | assert resp.body["graph"]["numberOfShards"] == 1 267 | assert resp.body["graph"]["smartGraphAttribute"] == "" 268 | assert resp.body["graph"]["_id"] == "_graphs/foo" 269 | assert is_binary(resp.body["graph"]["_rev"]) 270 | 271 | {:ok, edge_resp} = Graph.list_vertices(:arango, "foo") 272 | 273 | assert edge_resp.status_code == 200 274 | assert edge_resp.body["collections"] === ["baz"] 275 | 276 | remove_standard_graph() 277 | end 278 | 279 | test "replace_edge() replaces the given edge" do 280 | create_standard_graph() 281 | {:ok, %{body: %{"vertex" => %{"_id" => id1}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 282 | {:ok, %{body: %{"vertex" => %{"_id" => id2}}}} = Graph.create_vertex(:arango, "foo", "baz", %{my_key: "my_value"}) 283 | edge = %{_from: id1, _to: id2, my_key: "my_value"} 284 | {:ok, %{body: %{"edge" => %{"_id" => edge_id}}}} = Graph.create_edge(:arango, "foo", "my_edges", edge) 285 | {:ok, resp} = Graph.replace_edge(:arango, "foo", edge_id, %{_from: id1, _to: id2, other_key: "other_value"}) 286 | 287 | assert resp.status_code == 202 288 | assert resp.body["code"] == 202 289 | assert resp.body["error"] === false 290 | assert is_binary(resp.body["edge"]["_id"]) 291 | assert is_binary(resp.body["edge"]["_key"]) 292 | assert is_binary(resp.body["edge"]["_rev"]) 293 | assert is_binary(resp.body["edge"]["_oldRev"]) 294 | 295 | replaced_edge = 296 | resp.body["edge"] 297 | |> Map.delete("_oldRev") 298 | |> Map.put("other_key", "other_value") 299 | |> Map.put("_from", id1) 300 | |> Map.put("_to", id2) 301 | {:ok, edge_response} = Graph.get_edge(:arango, "foo", edge_id) 302 | 303 | assert edge_response.body["edge"] === replaced_edge 304 | 305 | remove_standard_graph() 306 | end 307 | 308 | test "replace_edges() replaces an edge definition in a graph" do 309 | create_standard_graph() 310 | {:ok, %{status_code: 202}} = Graph.add_vertices(:arango, "foo", %{collection: "bat"}) 311 | edge_def = %{collection: "my_edges", from: ["bar"], to: ["bat"]} 312 | {:ok, resp} = Graph.replace_edges(:arango, "foo", "my_edges", edge_def) 313 | 314 | assert resp.status_code == 202 315 | assert resp.body["code"] == 202 316 | assert resp.body["error"] == false 317 | assert resp.body["graph"]["name"] == "foo" 318 | assert resp.body["graph"]["edgeDefinitions"] == [%{"collection" => "my_edges", "from" => ["bar"], "to" => ["bat"]}] 319 | assert resp.body["graph"]["orphanCollections"] == ["baz"] 320 | assert resp.body["graph"]["isSmart"] == false 321 | assert resp.body["graph"]["numberOfShards"] == 1 322 | assert resp.body["graph"]["smartGraphAttribute"] == "" 323 | assert resp.body["graph"]["_id"] == "_graphs/foo" 324 | assert is_binary(resp.body["graph"]["_rev"]) 325 | 326 | remove_standard_graph() 327 | end 328 | 329 | test "replace_vertex() replaces the given vertex" do 330 | create_standard_graph() 331 | {:ok, %{body: %{"vertex" => %{"_id" => id}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 332 | {:ok, resp} = Graph.replace_vertex(:arango, "foo", id, %{other_key: "other_value"}) 333 | 334 | assert resp.status_code == 202 335 | assert resp.body["code"] == 202 336 | assert resp.body["error"] === false 337 | assert is_binary(resp.body["vertex"]["_id"]) 338 | assert is_binary(resp.body["vertex"]["_key"]) 339 | assert is_binary(resp.body["vertex"]["_rev"]) 340 | assert is_binary(resp.body["vertex"]["_oldRev"]) 341 | 342 | vertex = 343 | resp.body["vertex"] 344 | |> Map.delete("_oldRev") 345 | |> Map.put("other_key", "other_value") 346 | {:ok, vertex_response} = Graph.get_vertex(:arango, "foo", id) 347 | 348 | assert vertex_response.body["vertex"] === vertex 349 | 350 | remove_standard_graph() 351 | end 352 | 353 | test "update_edge() updates the given edge" do 354 | create_standard_graph() 355 | {:ok, %{body: %{"vertex" => %{"_id" => id1}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 356 | {:ok, %{body: %{"vertex" => %{"_id" => id2}}}} = Graph.create_vertex(:arango, "foo", "baz", %{my_key: "my_value"}) 357 | edge = %{_from: id1, _to: id2, my_key: "my_value"} 358 | {:ok, %{body: %{"edge" => %{"_id" => edge_id}}}} = Graph.create_edge(:arango, "foo", "my_edges", edge) 359 | {:ok, resp} = Graph.update_edge(:arango, "foo", edge_id, %{another_key: "another_value"}) 360 | 361 | assert resp.status_code == 202 362 | assert resp.body["code"] == 202 363 | assert resp.body["error"] === false 364 | assert is_binary(resp.body["edge"]["_id"]) 365 | assert is_binary(resp.body["edge"]["_key"]) 366 | assert is_binary(resp.body["edge"]["_rev"]) 367 | assert is_binary(resp.body["edge"]["_oldRev"]) 368 | 369 | updated_edge = 370 | resp.body["edge"] 371 | |> Map.delete("_oldRev") 372 | |> Map.put("my_key", "my_value") 373 | |> Map.put("another_key", "another_value") 374 | |> Map.put("_from", id1) 375 | |> Map.put("_to", id2) 376 | {:ok, edge_response} = Graph.get_edge(:arango, "foo", edge_id) 377 | 378 | assert edge_response.body["edge"] === updated_edge 379 | 380 | remove_standard_graph() 381 | end 382 | 383 | test "update_vertex() updates the given vertex" do 384 | create_standard_graph() 385 | {:ok, %{body: %{"vertex" => %{"_id" => id}}}} = Graph.create_vertex(:arango, "foo", "bar", %{my_key: "my_value"}) 386 | {:ok, resp} = Graph.update_vertex(:arango, "foo", id, %{another_key: "another_value"}) 387 | 388 | assert resp.status_code == 202 389 | assert resp.body["code"] == 202 390 | assert resp.body["error"] === false 391 | assert is_binary(resp.body["vertex"]["_id"]) 392 | assert is_binary(resp.body["vertex"]["_key"]) 393 | assert is_binary(resp.body["vertex"]["_rev"]) 394 | assert is_binary(resp.body["vertex"]["_oldRev"]) 395 | 396 | vertex = 397 | resp.body["vertex"] 398 | |> Map.delete("_oldRev") 399 | |> Map.put("my_key", "my_value") 400 | |> Map.put("another_key", "another_value") 401 | {:ok, vertex_response} = Graph.get_vertex(:arango, "foo", id) 402 | 403 | assert vertex_response.body["vertex"] === vertex 404 | 405 | remove_standard_graph() 406 | end 407 | 408 | defp create_standard_graph do 409 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "bar"}) 410 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "baz"}) 411 | {:ok, %{status_code: 200}} = Collection.create(:arango, %{name: "my_edges", type: 3}) 412 | edge_def = %{collection: "my_edges", from: ["bar"], to: ["baz"]} 413 | 414 | {:ok, %{status_code: 202}} = Graph.create(:arango, %{name: "foo", edgeDefinitions: [edge_def]}) 415 | end 416 | 417 | defp remove_standard_graph do 418 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "bar") 419 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "baz") 420 | {:ok, %{status_code: 200}} = Collection.remove(:arango, "my_edges") 421 | {:ok, %{status_code: 202}} = Graph.remove(:arango, "foo") 422 | end 423 | end 424 | -------------------------------------------------------------------------------- /lib/arangoex/graph.ex: -------------------------------------------------------------------------------- 1 | defmodule Arangoex.Graph do 2 | @moduledoc """ 3 | This module contains functions used to manage graph structures, vertex document collections, and edge document 4 | collections. 5 | """ 6 | 7 | @doc """ 8 | Add an edge definition to the given graph. 9 | 10 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph to 11 | which the edge definition is added. The `edges` parameter is a map containing the edge definition. 12 | 13 | ## Endpoint 14 | 15 | POST /_api/gharial/{graph_name}/edge 16 | 17 | ## Options 18 | 19 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 20 | 21 | ## Examples 22 | 23 | {:ok, conn} = Arangoex.start_link() 24 | edge_def = %{collection: "my_edges", from: ["foo", "bar"], to: ["baz"]} 25 | {:ok, resp} = Arangoex.Graph.add_edges(conn, "my_graph", edge_def) 26 | """ 27 | def add_edges(conn, graph_name, edges, opts \\ []) do 28 | Arangoex.request(conn, :post, "/_api/gharial/#{graph_name}/edge", %{}, %{}, edges, opts) 29 | end 30 | 31 | @doc """ 32 | Add a vertex collection to the given graph. Create the collection if it does not exist. 33 | 34 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph to 35 | which the vertex collection is added. The `vertices` parameter is a map containing a `:collection` property with the 36 | string name of the collection. 37 | 38 | ## Endpoint 39 | 40 | POST /_api/gharial/{graph_name}/vertex 41 | 42 | ## Options 43 | 44 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 45 | 46 | ## Examples 47 | 48 | {:ok, conn} = Arangoex.start_link() 49 | {:ok, resp} = Arangoex.Graph.add_vertices(conn, "my_graph", %{collection: "my_vertices"} 50 | 51 | """ 52 | def add_vertices(conn, graph_name, vertices, opts \\ []) do 53 | Arangoex.request(conn, :post, "/_api/gharial/#{graph_name}/vertex", %{}, %{}, vertices, opts) 54 | end 55 | 56 | @doc """ 57 | Create a new graph. 58 | 59 | The `conn` parameter is an ArangoDB connection PID. The `graph` parameter is a map containing the graph definition. 60 | 61 | ## Endpoint 62 | 63 | POST /_api/gharial 64 | 65 | ## Options 66 | 67 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 68 | 69 | ## Examples 70 | 71 | {:ok, conn} = Arangoex.start_link() 72 | edge_def = %{collection: "my_edges", from: ["my_vertices"], to: ["my_other_vertices"]} 73 | graph_def = %{name: "my_graph", edgeDefinitions: [edge_def]} 74 | Arangoex.Graph.create(graph_def) 75 | """ 76 | def create(conn, graph, opts \\ []) do 77 | Arangoex.request(conn, :post, "/_api/gharial", %{}, %{}, graph, opts) 78 | end 79 | 80 | @doc """ 81 | Create an edge in the given edge collection for the given graph. 82 | 83 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph to 84 | which the edge is added. The `collection_name` parameter is the string name of the edge collection to which the new 85 | edge is added. The `edge` parameter is a map containing the edge document. 86 | 87 | ## Endpoint 88 | 89 | POST /_api/gharial/{graph_name}/edge/{collection_name} 90 | 91 | ## Options 92 | 93 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 94 | 95 | ## Examples 96 | 97 | {:ok, conn} = Arangoex.start_link() 98 | {:ok, resp} = Arangoex.Graph.create_edge(conn, "my_graph", "my_edges, %{my_key: "my_value"}) 99 | """ 100 | def create_edge(conn, graph_name, collection_name, edge, opts \\ []) do 101 | Arangoex.request(conn, :post, "/_api/gharial/#{graph_name}/edge/#{collection_name}", %{}, %{}, edge, opts) 102 | end 103 | 104 | @doc """ 105 | Create a vertex in the given vertex collection for the given graph. 106 | 107 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph to 108 | which the vertex is added. The `collection_name` parameter is the string name of the vertex collection to which the 109 | new vertex is added. The `vertex` parameter is a map containing the vertex document. 110 | 111 | ## Endpoint 112 | 113 | POST /_api/gharial/{graph_name}/vertex/{collection_name} 114 | 115 | ## Options 116 | 117 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 118 | 119 | ## Examples 120 | 121 | {:ok, conn} = Arangoex.start_link() 122 | {:ok, resp} = Arangoex.Graph.create_vertex(conn, "my_graph", "my_vertices, %{my_key: "my_value"}) 123 | """ 124 | def create_vertex(conn, graph_name, collection_name, vertex, opts \\ []) do 125 | Arangoex.request(conn, :post, "/_api/gharial/#{graph_name}/vertex/#{collection_name}", %{}, %{}, vertex, opts) 126 | end 127 | 128 | @doc """ 129 | Return information about the given graph. 130 | 131 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the requested 132 | graph. 133 | 134 | ## Endpoint 135 | 136 | GET /_api/gharial/{graph_name} 137 | 138 | ## Options 139 | 140 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 141 | 142 | ## Examples 143 | 144 | {:ok, conn} = Arangoex.start_link() 145 | {:ok, resp} = Arangoex.Graph.get(conn, "foo") 146 | """ 147 | def get(conn, graph_name, opts \\ []) do 148 | Arangoex.request(conn, :get, "/_api/gharial/#{graph_name}", %{}, %{}, nil, opts) 149 | end 150 | 151 | @doc """ 152 | Return the given edge. 153 | 154 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph 155 | containing the edge. The `document_handle` parameter is the string `_id` of the edge document. 156 | 157 | ## Endpoint 158 | 159 | GET /_api/gharial/{graph_name}/edge/{collection_name}/{edge_key} 160 | 161 | ## Options 162 | 163 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 164 | 165 | ## Examples 166 | 167 | {:ok, conn} = Arangoex.start_link() 168 | {:ok, resp} = Arangoex.Graph.get_edge(conn, "my_graph", "my_edges/edge_key") 169 | """ 170 | def get_edge(conn, graph_name, document_handle, opts \\ []) do 171 | Arangoex.request(conn, :get, "/_api/gharial/#{graph_name}/edge/#{document_handle}", %{}, %{}, nil, opts) 172 | end 173 | 174 | @doc """ 175 | Return the given vertex. 176 | 177 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph 178 | containing the vertex. The `document_handle` parameter is the string `_id` of the vertex document. 179 | 180 | ## Endpoint 181 | 182 | GET /_api/gharial/{graph_name}/vertex/{collection_name}/{vetex_key} 183 | 184 | ## Options 185 | 186 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 187 | 188 | ## Examples 189 | 190 | {:ok, conn} = Arangoex.start_link() 191 | {:ok, resp} = Arangoex.Graph.get_vertex(conn, "my_graph", "my_vertices/vertex_key") 192 | """ 193 | def get_vertex(conn, graph_name, document_handle, opts \\ []) do 194 | Arangoex.request(conn, :get, "/_api/gharial/#{graph_name}/vertex/#{document_handle}", %{}, %{}, nil, opts) 195 | end 196 | 197 | @doc """ 198 | Return information about all graphs in the current database. 199 | 200 | The `conn` parameter is an ArangoDB connection PID. 201 | 202 | ## Endpoint 203 | 204 | GET /_api/gharial 205 | 206 | ## Options 207 | 208 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 209 | 210 | ## Examples 211 | 212 | {:ok, conn} = Arangoex.start_link() 213 | {:ok, resp} = Arangoex.Graph.list(conn) 214 | """ 215 | def list(conn, opts \\ []) do 216 | Arangoex.request(conn, :get, "/_api/gharial", %{}, %{}, nil, opts) 217 | end 218 | 219 | @doc """ 220 | Return a list of edge collections in a graph. 221 | 222 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph 223 | containing the edge collections. 224 | 225 | ## Endpoint 226 | 227 | GET /_api/gharial/{graph_name}/edge 228 | 229 | ## Options 230 | 231 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 232 | 233 | ## Examples 234 | 235 | {:ok, conn} = Arangoex.start_link() 236 | {:ok, resp} = Arangoex.Graph.list_edges(conn) 237 | """ 238 | def list_edges(conn, graph_name, opts \\ []) do 239 | Arangoex.request(conn, :get, "/_api/gharial/#{graph_name}/edge", %{}, %{}, nil, opts) 240 | end 241 | 242 | @doc """ 243 | Return a list of vertex collections in a graph. 244 | 245 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` paramter is the string name of the graph 246 | containing the vertex collections. 247 | 248 | ## Endpoint 249 | 250 | GET /_api/gharial/{graph_name}/vertex 251 | 252 | ## Options 253 | 254 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 255 | 256 | ## Examples 257 | 258 | {:ok, conn} = Arangoex.start_link() 259 | {:ok, resp} = Arangoex.Graph.list_vertices(conn) 260 | """ 261 | def list_vertices(conn, graph_name, opts \\ []) do 262 | Arangoex.request(conn, :get, "/_api/gharial/#{graph_name}/vertex", %{}, %{}, nil, opts) 263 | end 264 | 265 | @doc """ 266 | Remove the given graph. 267 | 268 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph to be 269 | removed. 270 | 271 | ## Endpoint 272 | 273 | DELETE /_api/gharial/{graph_name} 274 | 275 | ## Options 276 | 277 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 278 | 279 | ## Examples 280 | 281 | {:ok, conn} = Arangoex.start_link() 282 | {:ok, resp} = Arangoex.Graph.remove(conn, "foo") 283 | """ 284 | def remove(conn, graph_name, opts \\ []) do 285 | Arangoex.request(conn, :delete, "/_api/gharial/#{graph_name}", %{}, %{}, nil, opts) 286 | end 287 | 288 | @doc """ 289 | Remove the given edge from the graph. 290 | 291 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 292 | containing the edge to be removed. The `document_handle` parameter is the string `_id` of the edge document. 293 | 294 | ## Endpoint 295 | 296 | DELETE /_api/gharial/{graph_name}/edge/{document_handle} 297 | 298 | ## Options 299 | 300 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 301 | 302 | ## Examples 303 | 304 | {:ok, conn} = Arangoex.start_link() 305 | {:ok, resp} = Arangoex.Graph.remove_edge(conn, "foo", "my_edges/edge_key") 306 | """ 307 | def remove_edge(conn, graph_name, document_handle, opts \\ []) do 308 | Arangoex.request(conn, :delete, "/_api/gharial/#{graph_name}/edge/#{document_handle}", %{}, %{}, nil, opts) 309 | end 310 | 311 | @doc """ 312 | Remove the given edge definition from the graph. 313 | 314 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 315 | containing the edge definition to be removed. The `defninition_name` parameter is the string name of the edge 316 | definition. 317 | 318 | ## Endpoint 319 | 320 | DELETE /_api/gharial/{graph_name}/edge/{defnition_name} 321 | 322 | ## Options 323 | 324 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 325 | 326 | ## Examples 327 | 328 | {:ok, conn} = Arangoex.start_link() 329 | {:ok, resp} = Arangoex.Graph.remove_edges(conn, "foo", "my_edges") 330 | """ 331 | def remove_edges(conn, graph_name, definition_name, opts \\ []) do 332 | Arangoex.request(conn, :delete, "/_api/gharial/#{graph_name}/edge/#{definition_name}", %{}, %{}, nil, opts) 333 | end 334 | 335 | @doc """ 336 | Remove the given vertex from the graph. 337 | 338 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 339 | containing the vertex to be removed. The `document_handle` parameter is the string `_id` of the vertex document. 340 | 341 | ## Endpoint 342 | 343 | DELETE /_api/gharial/{graph_name}/vertex/{document_handle} 344 | 345 | ## Options 346 | 347 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 348 | 349 | ## Examples 350 | 351 | {:ok, conn} = Arangoex.start_link() 352 | {:ok, resp} = Arangoex.Graph.remove_vertex(conn, "foo", "my_vertices/vertex_key") 353 | """ 354 | def remove_vertex(conn, graph_name, document_handle, opts \\ []) do 355 | Arangoex.request(conn, :delete, "/_api/gharial/#{graph_name}/vertex/#{document_handle}", %{}, %{}, nil, opts) 356 | end 357 | 358 | @doc """ 359 | Remove the given vertex collection from the graph. 360 | 361 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 362 | containing the vertex collection to be removed. The `collection_name` parameter is the string name of the vertex 363 | collection. 364 | 365 | ## Endpoint 366 | 367 | DELETE /_api/gharial/{graph_name}/vertex/{collection_name} 368 | 369 | ## Options 370 | 371 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 372 | 373 | ## Examples 374 | 375 | {:ok, conn} = Arangoex.start_link() 376 | {:ok, resp} = Arangoex.Graph.remove_vertices(conn, "foo", "my_edges") 377 | """ 378 | def remove_vertices(conn, graph_name, collection_name, opts \\ []) do 379 | Arangoex.request(conn, :delete, "/_api/gharial/#{graph_name}/vertex/#{collection_name}", %{}, %{}, nil, opts) 380 | end 381 | 382 | @doc """ 383 | Replace the given edge in the graph. 384 | 385 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 386 | containing the edge to be replaced. The `document_handle` parameter is the string `_id` of the edge document. 387 | 388 | ## Endpoint 389 | 390 | PUT /_api/gharial/{graph_name}/edge/{document_handle} 391 | 392 | ## Options 393 | 394 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 395 | 396 | ## Examples 397 | 398 | {:ok, conn} = Arangoex.start_link() 399 | edge = %{_from: "vertices/vertex1", _to: "vertices/vertex2", new_key: "new_value"} 400 | {:ok, resp} = Arangoex.Graph.replace_edge(conn, "foo", "my_edges/edge_key", edge) 401 | """ 402 | def replace_edge(conn, graph_name, document_handle, edge, opts \\ []) do 403 | Arangoex.request(conn, :put, "/_api/gharial/#{graph_name}/edge/#{document_handle}", %{}, %{}, edge, opts) 404 | end 405 | 406 | @doc """ 407 | Replace the given edge definition in the graph. 408 | 409 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 410 | containing the edge definition to be replaced. The `defninition_name` parameter is the string name of the edge 411 | definition. 412 | 413 | ## Endpoint 414 | 415 | PUT /_api/gharial/{graph_name}/edge/{defnition_name} 416 | 417 | ## Options 418 | 419 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 420 | 421 | ## Examples 422 | 423 | {:ok, conn} = Arangoex.start_link() 424 | edge_def = %{collection: "my_edges", from: ["foo", "bar"], to: ["baz", "bat"]} 425 | {:ok, resp} = Arangoex.Graph.replace_edges(conn, "foo", "my_edges", edge_def) 426 | """ 427 | def replace_edges(conn, graph_name, definition_name, edges, opts \\ []) do 428 | Arangoex.request(conn, :put, "/_api/gharial/#{graph_name}/edge/#{definition_name}", %{}, %{}, edges, opts) 429 | end 430 | 431 | @doc """ 432 | Replace the given vertex in the graph. 433 | 434 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 435 | containing the vertex to be replaced. The `document_handle` parameter is the string `_id` of the vertex document. 436 | 437 | ## Endpoint 438 | 439 | PUT /_api/gharial/{graph_name}/vertex/{document_handle} 440 | 441 | ## Options 442 | 443 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 444 | 445 | ## Examples 446 | 447 | {:ok, conn} = Arangoex.start_link() 448 | {:ok, resp} = Arangoex.Graph.replace_vertex(conn, "foo", "my_vertices/vertex_key", %{new_key: "new_value"}) 449 | """ 450 | def replace_vertex(conn, graph_name, document_handle, vertex, opts \\ []) do 451 | Arangoex.request(conn, :put, "/_api/gharial/#{graph_name}/vertex/#{document_handle}", %{}, %{}, vertex, opts) 452 | end 453 | 454 | @doc """ 455 | Update the given edge in the graph. 456 | 457 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 458 | containing the edge to be updated. The `document_handle` parameter is the string `_id` of the edge document. 459 | 460 | ## Endpoint 461 | 462 | PATCH /_api/gharial/{graph_name}/edge/{document_handle} 463 | 464 | ## Options 465 | 466 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 467 | 468 | ## Examples 469 | 470 | {:ok, conn} = Arangoex.start_link() 471 | {:ok, resp} = Arangoex.Graph.update_edge(conn, "foo", "my_edges/edge_key", %{new_key: "new_value}) 472 | """ 473 | def update_edge(conn, graph_name, document_handle, edge, opts \\ []) do 474 | Arangoex.request(conn, :patch, "/_api/gharial/#{graph_name}/edge/#{document_handle}", %{}, %{}, edge, opts) 475 | end 476 | 477 | @doc """ 478 | Update the given vertex in the graph. 479 | 480 | The `conn` parameter is an ArangoDB connection PID. The `graph_name` parameter is the string name of the graph 481 | containing the vertex to be updated. The `document_handle` parameter is the string `_id` of the vertex document. 482 | 483 | ## Endpoint 484 | 485 | PATCH /_api/gharial/{graph_name}/vertex/{document_handle} 486 | 487 | ## Options 488 | 489 | See the "Shared Options" in the `Arangoex` module documentation for additional options. 490 | 491 | ## Examples 492 | 493 | {:ok, conn} = Arangoex.start_link() 494 | {:ok, resp} = Arangoex.Graph.update_vertex(conn, "foo", "my_vertices/vertex_key", %{new_key: "new_value"}) 495 | """ 496 | def update_vertex(conn, graph_name, document_handle, vertex, opts \\ []) do 497 | Arangoex.request(conn, :patch, "/_api/gharial/#{graph_name}/vertex/#{document_handle}", %{}, %{}, vertex, opts) 498 | end 499 | end 500 | --------------------------------------------------------------------------------