# `Sofa`
[🔗](https://github.com/skunkwerks/sofa/blob/0.5.1/lib/sofa.ex#L1)

Documentation for `Sofa`, a test-driven idiomatic Apache CouchDB client.

> If the only tool you have is CouchDB, then
> everything looks like `{:ok, :relax}`

## Examples

    iex> Sofa.init() |> Sofa.client() |> Sofa.connect!()

    %{
      "couchdb" => "Welcome",
      "features" => ["access-ready", "partitioned", "pluggable-storage-engines",
        "reshard", "scheduler"],
      "git_sha" => "ce596c65d",
      "uuid" => "59c032d3a6adcd5b44315137a124bf69",
      "vendor" => %{"name" => "FreeBSD"},
      "version" => "3.1.1"
    }

# `t`

```elixir
@type t() :: %Sofa{
  auth: any(),
  client: nil | Req.Request.t(),
  database: nil | binary(),
  features: nil | list(),
  timeout: nil | integer(),
  uri: nil | URI.t(),
  uuid: nil | binary(),
  vendor: nil | map(),
  version: nil | binary()
}
```

# `active_tasks`

```elixir
@spec active_tasks(t()) :: {:error, any()} | {:ok, [map()]}
```

Get _active_tasks. Only available to admin users.

# `all_dbs`

```elixir
@spec all_dbs(t()) :: {:error, any()} | {:ok, [String.t()]}
```

List all databases. Only available to admin users.

# `auth_info`

```elixir
@spec auth_info(nil | String.t()) ::
  %{} | %{username: String.t(), password: String.t()}
```

Returns user & password credentials extracted from a typical %URI{} userinfo
field, as basic auth credentials. Currently only supports
BasicAuth user:password combination.

## Examples

    iex> Sofa.auth_info("admin:password")
    %{username: "admin", password: "password"}

    iex> Sofa.auth_info("blank:")
    %{username: "blank", password: ""}

    iex> Sofa.auth_info("garbage")
    %{}

# `client`

```elixir
@spec client(t(), keyword()) :: t()
```

Builds a reusable `Req.Request` HTTP client, with basic auth credentials
taken from the URI, and stores it in the supplied `%Sofa{}` struct.

Any `Req.new/1` options may be passed to override the defaults, for example
`receive_timeout: 5_000`, `plug: MyPlug` for `Req.Test`, or `inet6: false`
to disable the default IPv6-first, IPv4-fallback connection behaviour.

Retries are disabled, as CouchDB responses are expected to be fast and
callers typically want to see errors immediately.

# `connect`

```elixir
@spec connect(String.t() | t()) :: {:ok, t()} | {:error, any()}
```

Given an existing %Sofa{} struct, or a prepared URI, attempts to connect
to the CouchDB instance, and returns an updated %Sofa{} to use in future
connections to this server, using the same HTTP credentials.

Returns an updated `{:ok, %Sofa{}}` on success, or `{:error, reason}`, if
for example, the URL is unreachable, times out, supplied credentials are
rejected by CouchDB, or returns unexpected HTTP status codes.

# `connect!`

```elixir
@spec connect!(String.t() | t()) :: t()
```

Bang! wrapper around Sofa.connect/1. Raises exceptions on error.

# `init`

```elixir
@spec init(uri :: String.t() | URI.t()) :: t()
```

Takes an optional parameter, the CouchDB uri, and returns a struct
containing the usual CouchDB server properties. The URI may be given
as a string or as a %URI struct.

This should be piped into `Sofa.client/1` to create the HTTP client,
which is stored inside the struct with correct authentication information.

## Examples

    iex> Sofa.init("https://very:Secure@foreignho.st:6984/")

    %Sofa{
      auth: "very:Secure",
      features: nil,
      uri: %URI{
        authority: "very:Secure@foreignho.st:6984",
        fragment: nil,
        host: "foreignho.st",
        path: "/",
        port: 6984,
        query: nil,
        scheme: "https",
        userinfo: "very:Secure"
      },
      uuid: nil,
      vendor: nil,
      version: nil
    }

# `raw`

```elixir
@spec raw(
  t(),
  String.t(),
  atom(),
  keyword() | map(),
  map() | binary(),
  [{String.t(), String.t()}] | map(),
  keyword()
) :: {:error, any()} | {:ok, t(), Sofa.Response.t()}
```

Minimal wrapper around native CouchDB HTTP API, allowing an escape hatch
for raw functionality, and as the core abstraction layer for Sofa itself.

A map `body` is sent as JSON, a binary body is sent as-is. JSON responses
are decoded into maps, and CouchDB error responses, those with both an
`error` and a `reason` key, are returned as `{:error, %Sofa.Response{}}`.
Transport failures such as `:econnrefused` or `:timeout` are returned as
`{:error, %Req.TransportError{}}`.

Any further `Req.request/2` options go last, for the few requests that are
not JSON in and out: `raw: true` keeps a body as bytes, `into:` streams it
to a sink, and `body:` sends an enumerable as a chunked upload. The `path`
is sent as given. See `Sofa.Cushion.path/1` for encoding its segments.

# `raw!`

```elixir
@spec raw!(t(), String.t(), atom(), keyword() | map(), map() | binary()) ::
  Sofa.Response.t()
```

Bang! wrapper around Sofa.raw/1 raises exceptions on error.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
