mirror of
https://github.com/mitchell/shortnr.git
synced 2025-12-17 05:17:22 +00:00
Project architecture boilerplate
This commit is contained in:
commit
7ff70d452a
12 changed files with 261 additions and 0 deletions
35
lib/router.ex
Normal file
35
lib/router.ex
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
defmodule Shortnr.Router do
|
||||
use Plug.ErrorHandler
|
||||
use Plug.Router
|
||||
|
||||
require Logger
|
||||
|
||||
alias Shortnr.Transport.{Json, Http}
|
||||
alias Shortnr.Url
|
||||
|
||||
plug(Plug.Logger, log: :debug)
|
||||
plug(:match)
|
||||
plug(:dispatch)
|
||||
|
||||
post "/urls" do
|
||||
conn
|
||||
|> Json.decode_request(Url.CreateRequest)
|
||||
|> Http.handle(&Url.create(&1, Url.Repo.DETS))
|
||||
|> Json.encode_response()
|
||||
|> Http.send(:created, conn)
|
||||
end
|
||||
|
||||
match _ do
|
||||
{:error, {:not_found, "route not found"}, conn}
|
||||
|> Json.encode_response()
|
||||
|> Http.send(:ignore, conn)
|
||||
end
|
||||
|
||||
def handle_errors(conn, %{kind: _kind, reason: reason, stack: stack}) do
|
||||
Logger.error(inspect(reason), stack: stack)
|
||||
|
||||
{:error, {:internal_server_error, "internal server error"}, conn}
|
||||
|> Json.encode_response()
|
||||
|> Http.send(:ignore, conn)
|
||||
end
|
||||
end
|
||||
28
lib/shortnr.ex
Normal file
28
lib/shortnr.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Shortnr do
|
||||
@moduledoc """
|
||||
Documentation for Shortnr.
|
||||
"""
|
||||
|
||||
use Application
|
||||
require Logger
|
||||
|
||||
@impl true
|
||||
def start(_type, _args) do
|
||||
port = port()
|
||||
|
||||
children = [
|
||||
{Plug.Cowboy, scheme: :http, plug: Shortnr.Router, options: [port: port]}
|
||||
]
|
||||
|
||||
Logger.info("server starting", port: port)
|
||||
Supervisor.start_link(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
@spec port() :: integer()
|
||||
defp port do
|
||||
case Application.fetch_env(:service, :port) do
|
||||
{:ok, port} -> port
|
||||
_ -> 4000
|
||||
end
|
||||
end
|
||||
end
|
||||
36
lib/transport/http.ex
Normal file
36
lib/transport/http.ex
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
defmodule Shortnr.Transport.Http do
|
||||
import Plug.Conn
|
||||
|
||||
@type ok_error :: {:ok, term(), Plug.Conn.t()} | error()
|
||||
@type error :: {:error, {atom(), String.t()}, Plug.Conn.t()}
|
||||
|
||||
@spec handle(ok_error(), (term() -> ok_error())) :: ok_error()
|
||||
def handle(error = {:error, _sub_error, _conn}, _func), do: error
|
||||
|
||||
def handle({:ok, request, conn}, func) do
|
||||
case func.(request) do
|
||||
{:ok, response} -> {:ok, response, conn}
|
||||
{:error, error_value} -> convert_error(error_value, conn)
|
||||
end
|
||||
end
|
||||
|
||||
defp convert_error({:invalid_argument, message}, conn),
|
||||
do: {:error, {:bad_request, message}, conn}
|
||||
|
||||
defp convert_error(_, conn), do: {:error, {:internal_server_error, "unknown error"}, conn}
|
||||
|
||||
@spec send(ok_error(), atom(), Plug.Conn.t()) :: Plug.Conn.t()
|
||||
def send({:error, {status, body}, _conn}, _success_status, original_conn) do
|
||||
send_resp(original_conn, status_to_code(status), body)
|
||||
end
|
||||
|
||||
def send({:ok, body, conn}, success_status, _original_conn) do
|
||||
send_resp(conn, status_to_code(success_status), body)
|
||||
end
|
||||
|
||||
defp status_to_code(:ok), do: 200
|
||||
defp status_to_code(:created), do: 201
|
||||
defp status_to_code(:bad_request), do: 400
|
||||
defp status_to_code(:not_found), do: 404
|
||||
defp status_to_code(:internal_server_error), do: 500
|
||||
end
|
||||
28
lib/transport/json.ex
Normal file
28
lib/transport/json.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Shortnr.Transport.Json do
|
||||
import Plug.Conn
|
||||
alias Shortnr.Transport.Http
|
||||
|
||||
@spec decode_request(Plug.Conn.t(), module()) :: Http.ok_error()
|
||||
def decode_request(conn, struct_module) do
|
||||
{:ok, body, conn} = read_body(conn)
|
||||
{:ok, params} = Jason.decode(body)
|
||||
|
||||
params_list =
|
||||
params
|
||||
|> Map.to_list()
|
||||
|> Enum.map(fn {key, value} -> {String.to_atom(key), value} end)
|
||||
|
||||
{:ok, struct(struct_module, params_list), conn}
|
||||
end
|
||||
|
||||
@spec encode_response(Http.ok_error()) :: Http.ok_error()
|
||||
def encode_response({:error, {status, response}, conn}) do
|
||||
{:ok, json_body} = Jason.encode(%{data: response})
|
||||
{:error, {status, json_body}, conn}
|
||||
end
|
||||
|
||||
def encode_response({:ok, response, conn}) do
|
||||
{:ok, json_body} = Jason.encode(%{data: response})
|
||||
{:ok, json_body, conn}
|
||||
end
|
||||
end
|
||||
4
lib/transport/transport.ex
Normal file
4
lib/transport/transport.ex
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
defmodule Shortnr.Transport do
|
||||
@type error :: {:error, {atom(), String.t()}}
|
||||
@type ok_error :: {:ok, term()} | error()
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue