Transformation: Convert Response

We’ll need a JSON library to convert the response into a data structure. Searching hex.pm, I found the poison library (no relation to HTTPoison), so let’s add its dependency to our mix.exs file.[23]

project/2/issues/mix.exs
 defp​ deps ​do
  [
  { ​:httpoison​, ​"​​~> 1.0.0"​ },
  { ​:poison​, ​"​​~> 3.1"​ },
  ]
 end

Run mix deps.get, and you’ll end up with poison installed.

To convert the body from a string, we call the Poison.Parser.parse! function when we return the message from the GitHub API:

project/3/issues/lib/issues/github_issues.ex
 def​ handle_response({ _, %{​status_code:​ status_code, ​body:​ body}}) ​do
  {
  status_code |> check_for_error(),
  body |> Poison.Parser.parse!()
  }
 end
 
 defp​ check_for_error(200), ​do​: ​:ok
 defp​ check_for_error(_), ​do​: ​:error

We also have to deal with a possible error response from the fetch, so back in the CLI module we write a function that decodes the body and returns it on a success response; the function extracts the error from the body and displays it otherwise.

project/3/issues/lib/issues/cli.ex
 def​ process({user, project, _count}) ​do
  Issues.GithubIssues.fetch(user, project)
» |> decode_response()
 end
 
 def​ decode_response({​:ok​, body}), ​do​: body
 
 def​ decode_response({​:error​, error}) ​do
  IO.puts ​"​​Error fetching from Github: ​​#{​error[​"​​message"​]​}​​"
  System.halt(2)
 end

The JSON that GitHub returns for a successful response is a list of maps, where each map in the list contains a GitHub issue.

Application Configuration

Before we move on, there’s one little tweak I’d like to make. The issues_url function hard-codes the GitHub URL. Let’s make this configurable.

Remember that when we created the project using mix new, it added a config/ directory containing config.exs. That file stores application-level configuration.

It should start with the line

 use​ Mix.Config

We then write configuration information for each of the applications in our project. Here we’re configuring the Issues application, so we write this code:

project/3a/issues/config/config.exs
 use​ Mix.Config
 config ​:issues​, ​github_url:​ ​"​​https://api.github.com"

Each config line adds one or more key/value pairs to the given application’s environment. If you have multiple lines for the same application, they accumulate, with duplicate keys in later lines overriding values from earlier ones.

In our code, we use Application.get_env to return a value from the environment.

project/3a/issues/lib/issues/github_issues.ex
 # use a module attribute to fetch the value at compile time
 @github_url Application.get_env(​:issues​, ​:github_url​)
 
 def​ issues_url(user, project) ​do
 "​​#{​@github_url​}​​/repos/​​#{​user​}​​/​​#{​project​}​​/issues"
 end

Because the application environment is commonly used in Erlang code, you’ll find yourself using the configuration facility to configure code you import, as well as code you write.

Sometimes you may want to vary the configuration, perhaps depending on your application’s environment. One way is to use the import_config function, which reads configuration from a file. If your config.exs contains

 use​ Mix.Config
 
 import_config ​"​​#{​Mix.env​}​​.exs"

then Elixir will read dev.exs, test.exs, or prod.exs, depending on your environment.

You can override the default config file name (config/config.exs) using the --config option to elixir.