README.md

# Client

HTTPClient is a simple wrapper around HTTPoison using Poison and
HTTPoisonFormData to format payloads.

## Installation

The package can be installed as:

  1. Add `http_client` to your list of dependencies in `mix.exs`:

    ```elixir
    def deps do
      [{:http_client, "~> 0.1.0"}]
    end
    ```

  2. Ensure `client` is started before your application:

    ```elixir
    def application do
      [applications: [:client]]
    end
    ```

## Configuration
none...

## Usage

It is important to understand how this client works in order to properly use it.
It provides two implementations of a single function `do_request/6`, which takes
the arguments explained below:

| Argument   | description |
| ---------- | ----------- |
| `href`     | The URL of the resource to be queried |
| `payload`  | A Map, Struct, or List to be sent to the server |
| `headers`  | The headers to be sent with the query |
| `encoder`  | This is an encoder from the Client package, a list of encoders is provided below |
| `decoder`  | This is a decoder from the Client package, a list of decoders is proved below |
| `action`   | This is an HTTPoison verb. Usage defined below |

### Example
```elixir
data = Client.do_request(
  "https://httpbin.org",
  %{"key" => "value", "key2" => ["value1", "value2"]},
  %{"Header" => "Header/Value"},
  Client.Encoders.JSON,
  Client.Decoders.JSON,
  &Client.post(&1, &2, &3)
)

assert data == {
  :ok,
  %{
    "args" => %{},
    "data" => "{\"key2\":[\"value1\",\"value2\"],\"key\":\"value\"}",
    "files" => %{},
    "form" => %{},
    "headers" => %{
      "Accept" => "application/json",
      "Content-Length" => "42",
      "Content-Type" => "application/json",
      "Header" => "Header/Value",
      "Host" => "httpbin.org",
      "User-Agent" => "hackney/1.6.1"
    },
    "json" => %{
      "key" => "value",
      "key2" => ["value1", "value2"]
    },
    "origin" => "72.182.43.183",
    "url" => "https://httpbin.org/post"
  }
}
```

### Encoders
#### Provided Encoders
 - GETURLEncoded
    This uses FormData to prepare a url-encoded payload for GET requests, it
    does not set custom headers because HTTPoison and Hackney do this
    automatically.
 - JSON
    This uses Poison to prepare a binary payload for POST, PATCH, and PUT
    requests, it sets a custom `Content-Type` header to `application/json`.
 - Multipart
    This uses FormData to prepare a multipart payload for POST, PATCH, PUT
    requests. It does not set custom headers because HTTPoison and Hackney do
    this automatically.
 - NilEncoder
    This does not encode any data, it is only useful for documentation purposes
    when using HTTPoison's delete/2 function, since DELETE requests do not carry
    payloads.
 - URLEncoded
    This uses FormData to prepare a url-encoded payload for POST, PATCH< and PUT
    requests. It does not set custom headers because HTTPoison and Hackney do
    this automatically.
#### Extension
You can provide your own encoders by making a module that implements the
`Client.Encoders` behaviour.

##### Example
```elixir
defmodule MyCustomEncoder do
  @behaviour Client.Encoders

  def headers, do: %{"Content-Type" => "application/custom-data"}
  def encode(payload), do: MyEncoder.encode(payload)
  def encode!(payload), do: MyEncoder.encode!(payload)
end
```

See the `Client.Encoders` behaviour for exact typespecs.

### Deocoders
#### Provided Decoders
 - JSON
    This uses Poison to decode responses from a server. It sets a custom
    `Accept` header to `application/json` so the server knows what data
    to respond with.
#### Extension
You can provide your own decoders by making a module that implements the
`Client.Decoders` behaviour.

##### Example
```elixir
defmodule MyCustomDecoder do
  @behaviour Client.Encoders

  def headers, do: %{"Accept" => "application/custom-data"}
  def decode(data), do: MyDecoder.decode(data)
  def decode!(data), do: MyDecoder.decode!(data)
end
```

See the `Client.Decoders` behaviour for exact typespecs.

### Actions
Actions are the part that actually make the HTTP Request, if that is what you
choose to do with this library. It is failry generic. Some actions are provided.
#### Provided Actions
 - `Client.get/3` / `Client.get!/3`
 - `Client.post/3` / `Client.post!/3`
 - `Client.patch/3` / `Client.patch!/3`
 - `Client.put/3` / `Client.put!/3`
 - `Client.delete/3` / `Client.delete!/3`
The provided actions are all simple wrappers around HTTPoison to make the
arguments resemble what the callback requires in `do_request/6` and
`do_request!/6`

#### Notes
When using `do_request/6`, your actions all need to return a tuple of the format
`{:ok, data}` or `{:error, reason}`, any other formats will not be properly
handled by `do_request/6`.

When using `do_request!/6`, your actions must all return `data` directly,
outside of the tuple used in the safer version. The reason for this is we expect
errors in this case to be raised rather than returned.

### Helpers
You can make the process of calling functions like these easier if you know
about how you want to interact with the server.
```elixir
def get_json(href, payload, headers)
  do_request(href, payload, headers,
    Client.Encoders.GETURLEncoded,
    Client.Decoders.JSON,
    &Client.get(&1, &2, &3)
  )
end

def post_json(href, payload, headers)
  do_request(href, payload, headers,
    Client.Encoders.JSON,
    Client.Decoders.JSON,
    &Client.post(&1, &2, &3)
  )
end

... # etc

def delete_json(href, headers)
  do_request(href, %{}, headers,
    Client.Encoders.NilEncoder,
    Client.Decoders.JSON,
    &Client.delete(&1, &2, &3)
  )
end
```
Helper functions like these make working with HTTP incredibly easy.

## License
```
Copyright © 2016 Riley Trautman, <asonix.dev@gmail.com>

This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the LICENSE file for more details.
```