Skip to content

Commit

Permalink
General CRUD
Browse files Browse the repository at this point in the history
- Add basic CRUD.
  • Loading branch information
thelastinuit committed Aug 8, 2021
1 parent b7d8757 commit 3d05c26
Show file tree
Hide file tree
Showing 29 changed files with 480 additions and 38 deletions.
5 changes: 2 additions & 3 deletions .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
excluded: [
~r"/_build/",
~r"/deps/",
~r"/node_modules/",
~r"/node_modules/"
]
},
#
Expand Down Expand Up @@ -87,8 +87,7 @@
{Credo.Check.Readability.AliasOrder, []},
{Credo.Check.Readability.FunctionNames, []},
{Credo.Check.Readability.LargeNumbers, []},
{Credo.Check.Readability.MaxLineLength,
[priority: :low, max_length: 120]},
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
{Credo.Check.Readability.ModuleAttributeNames, []},
{Credo.Check.Readability.ModuleDoc, false},
{Credo.Check.Readability.ModuleNames, []},
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
elixir: [1.11]
otp: [23.2]
elixir: [1.12]
otp: [24]
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
Expand Down Expand Up @@ -60,8 +60,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
elixir: [1.11]
otp: [23.2]
elixir: [1.12]
otp: [24]
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
Expand Down Expand Up @@ -100,8 +100,8 @@ jobs:
strategy:
fail-fast: false
matrix:
elixir: [1.11]
otp: [23.2]
elixir: [1.12]
otp: [24]
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.6.0
Expand Down Expand Up @@ -133,4 +133,8 @@ jobs:
- name: Run test
env:
MIX_ENV: test
FACTURAPI_USER_KEY: fakeuserkey
FACTURAPI_VENTUP_ORGANIZATION_API_LIVE_KEY: fakeapilivekey
FACTURAPI_VENTUP_ORGANIZATION_API_TEST_KEY: faketestkey
FACTURAPI_VENTUP_ORGANIZATION_ID: fakeorganizationid
run: mix test --trace --slowest 10
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ precommit:
gcdeps:
mix deps.get && mix deps.compile

test:
t:
MIX_ENV=test mix test --trace --max-failures 1

test-watcher:
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,3 @@ end
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/ex_facturapi](https://hexdocs.pm/ex_facturapi).

1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use Mix.Config
124 changes: 114 additions & 10 deletions lib/ex_facturapi.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,122 @@
defmodule ExFacturapi do
@moduledoc """
Documentation for `ExFacturapi`.
@default_api_endpoint "https://www.facturapi.io/v1/"
@default_http_client HTTPoison
@client_version Mix.Project.config()[:version]

alias ExFacturapi.{
APIConnectionError,
AuthenticationError,
InvalidRequestError,
RateLimitError,
APIError
}

@missing_secret_key_error_message """
Missing Keys, please set them up
"""

@doc """
Hello world.
@spec version() :: String.t()
def version do
@client_version
end

## Examples
@spec request(atom(), String.t(), map()) :: map()
def request(action, endpoint, data) when action in [:get, :post, :put, :delete] do
action
|> http_client().request(request_url(endpoint, data), "", create_headers())
|> handle_response
end

iex> ExFacturapi.hello()
:world
defp http_client do
Application.get_env(:facturapi, :http_client) ||
@default_http_client
end

"""
def hello do
:world
defp get_api_endpoint do
System.get_env("FACTURAPI_ENDPOINT") ||
Application.get_env(:facturapi, :endpoint) ||
@default_api_endpoint
end

defp request_url(endpoint) do
Path.join(get_api_endpoint(), endpoint)
end

defp request_url(endpoint, []) do
Path.join(get_api_endpoint(), endpoint)
end

defp request_url(endpoint, data) do
base_url = request_url(endpoint)
query_params = ExFacturapi.Utils.encode_data(data)
"#{base_url}?#{query_params}"
end

defp create_headers do
[
{"Authorization", "Basic #{Base.encode64(get_api_key() <> ":")}"},
{"Content-Type", "application/json"}
]
end

defp get_api_key do
System.get_env("FACTURAPI_USER_KEY") ||
Application.get_env(:facturapi, :user_key) ||
raise AuthenticationError, message: @missing_secret_key_error_message
end

defp get_api_live_key do
System.get_env("FACTURAPI_LIVE_KEY") ||
Application.get_env(:facturapi, :live_key) ||
raise AuthenticationError, message: @missing_secret_key_error_message
end

defp get_api_test_key do
System.get_env("FACTURAPI_TEST_KEY") ||
Application.get_env(:facturapi, :test_key) ||
raise AuthenticationError, message: @missing_secret_key_error_message
end

defp get_organization_id do
System.get_env("FACTURAPI_ORGANIZATION_ID") ||
Application.get_env(:facturapi, :organization_id) ||
raise AuthenticationError, message: @missing_secret_key_error_message
end

defp handle_response({:ok, %{body: body, status_code: 200}}) do
{:ok, process_response_body(body)}
end

defp handle_response({:ok, %{body: body, status_code: code}}) do
%{"message" => message} =
error =
body
|> process_response_body
|> Map.fetch!("error")

error_struct =
case code do
code when code in [400, 404] ->
%InvalidRequestError{message: message, param: error["param"]}

401 ->
%AuthenticationError{message: message}

429 ->
%RateLimitError{message: message}

_ ->
%APIError{message: message}
end

{:error, error_struct}
end

defp handle_response({:error, %HTTPoison.Error{reason: reason}}) do
%APIConnectionError{message: "Network Error: #{reason}"}
end

defp process_response_body(body) do
Poison.decode!(body)
end
end
58 changes: 58 additions & 0 deletions lib/ex_facturapi/api.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
defmodule ExFacturapi.API do
defmacro __using__(opts) do
quote do
if :create in unquote(opts) do
@doc """
Create a(n) #{__MODULE__ |> to_string |> String.split(".") |> List.last()}
"""
@spec create(map()) :: map()
def create(data) do
ExFacturapi.request(:post, endpoint(), data)
end
end

if :retrieve in unquote(opts) do
@doc """
Retrive a(n) #{__MODULE__ |> to_string |> String.split(".") |> List.last()} by its ID
"""
@spec retrieve(binary()) :: map()
def retrieve(id) when is_bitstring(id) do
resource_url = Path.join(endpoint(), id)
ExFacturapi.request(:get, resource_url, [])
end
end

if :update in unquote(opts) do
@doc """
Update a(n) #{__MODULE__ |> to_string |> String.split(".") |> List.last()}
"""
@spec update(binary(), map()) :: map()
def update(id, data) when is_bitstring(id) do
resource_url = Path.join(endpoint(), id)
ExFacturapi.request(:put, resource_url, data)
end
end

if :list in unquote(opts) do
@doc """
List all #{__MODULE__ |> to_string |> String.split(".") |> List.last()}s
"""
@spec list(list()) :: map()
def list(pagination_opts \\ []) when is_list(pagination_opts) do
ExFacturapi.request(:get, endpoint(), pagination_opts)
end
end

if :delete in unquote(opts) do
@doc """
Delete a(n) #{__MODULE__ |> to_string |> String.split(".") |> List.last()}
"""
@spec delete(binary(), map()) :: map()
def delete(id, data \\ []) when is_bitstring(id) do
resource_url = Path.join(endpoint(), id)
ExFacturapi.request(:delete, resource_url, data)
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/catalogs.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Catalogs do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/catalogs"
end
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/clients.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Clients do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/clients"
end
end
6 changes: 6 additions & 0 deletions lib/ex_facturapi/errors/api_connection_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
defmodule ExFacturapi.APIConnectionError do
@moduledoc """
Failure to connect to Facturapi's API.
"""
defexception type: "api_connection_error", message: nil
end
7 changes: 7 additions & 0 deletions lib/ex_facturapi/errors/api_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule ExFacturapi.APIError do
@moduledoc """
API errors cover any other type of problem (e.g., a temporary problem with
Facturapi's servers) and are extremely uncommon.
"""
defexception type: "api_error", message: nil
end
6 changes: 6 additions & 0 deletions lib/ex_facturapi/errors/authentication_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
defmodule ExFacturapi.AuthenticationError do
@moduledoc """
Failure to properly authenticate yourself in the request.
"""
defexception type: "authentication_error", message: nil
end
6 changes: 6 additions & 0 deletions lib/ex_facturapi/errors/invalid_request_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
defmodule ExFacturapi.InvalidRequestError do
@moduledoc """
Invalid request errors arise when your request has invalid parameters.
"""
defexception type: "invalid_request_error", message: nil, param: nil
end
6 changes: 6 additions & 0 deletions lib/ex_facturapi/errors/rate_limit_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
defmodule ExFacturapi.RateLimitError do
@moduledoc """
Too many requests hit the API too quickly.
"""
defexception type: "rate_limit_error", message: nil
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/invoices.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Invoices do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/invoices"
end
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/organizations.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Organizations do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/organizations"
end
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/products.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Products do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/products"
end
end
8 changes: 8 additions & 0 deletions lib/ex_facturapi/receipts.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
defmodule ExFacturapi.Receipts do
use ExFacturapi.API, [:retrieve, :update, :create, :list, :delete]

@spec endpoint() :: String.t()
def endpoint do
"/receipts"
end
end
Loading

0 comments on commit 3d05c26

Please sign in to comment.