mirror of https://github.com/mitchell/shortnr.git
Add Endpoints behaviour and refactor router and related methods
This commit is contained in:
parent
1502d10cdc
commit
98684feb2d
|
@ -0,0 +1,11 @@
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Atom':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.BitString':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Float':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Function':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Integer':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.List':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Map':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.PID':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Port':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Reference':'__impl__'/1
|
||||||
|
:0: Unknown function 'Elixir.Shortnr.Transport.Text.Encodable.Tuple':'__impl__'/1
|
|
@ -3,57 +3,27 @@ defmodule Shortnr.Router do
|
||||||
This module contains the Router for the Shortnr application. Do not import, other than
|
This module contains the Router for the Shortnr application. Do not import, other than
|
||||||
Application entry.
|
Application entry.
|
||||||
"""
|
"""
|
||||||
use Plug.ErrorHandler
|
alias Shortnr.Transport.HTTP
|
||||||
use Plug.Router
|
alias Shortnr.URL
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Shortnr.Transport.{HTTP, Text}
|
use Plug.ErrorHandler
|
||||||
alias Shortnr.URL
|
use Plug.Router
|
||||||
|
|
||||||
plug(Plug.Logger, log: :debug)
|
plug(Plug.Logger, log: :debug)
|
||||||
plug(:match)
|
plug(:match)
|
||||||
plug(:dispatch)
|
plug(:dispatch)
|
||||||
|
|
||||||
get "/" do
|
# BEGIN URL routes
|
||||||
conn
|
post("/urls/:url", do: URL.Endpoints.select(conn, :create, url))
|
||||||
|> HTTP.wrap()
|
|
||||||
|> HTTP.handle(fn -> URL.list(URL.Repo.ETS) end)
|
|
||||||
|> Text.encode_response()
|
|
||||||
|> HTTP.send(:ok)
|
|
||||||
end
|
|
||||||
|
|
||||||
post "/urls/:url" do
|
get("/", do: URL.Endpoints.select(conn, :list))
|
||||||
conn
|
get("/urls", do: URL.Endpoints.select(conn, :list))
|
||||||
|> HTTP.wrap(url)
|
get("/:id", do: URL.Endpoints.select(conn, :get, id))
|
||||||
|> HTTP.handle(&URL.create(&1, URL.Repo.ETS))
|
|
||||||
|> Text.encode_response()
|
|
||||||
|> HTTP.send(:created)
|
|
||||||
end
|
|
||||||
|
|
||||||
get "/urls" do
|
delete("/:id", do: URL.Endpoints.select(conn, :delete, id))
|
||||||
conn
|
# END
|
||||||
|> HTTP.wrap()
|
|
||||||
|> HTTP.handle(fn -> URL.list(URL.Repo.ETS) end)
|
|
||||||
|> Text.encode_response()
|
|
||||||
|> HTTP.send(:ok)
|
|
||||||
end
|
|
||||||
|
|
||||||
get "/:id" do
|
|
||||||
conn
|
|
||||||
|> HTTP.wrap(id)
|
|
||||||
|> HTTP.handle(&URL.get(&1, URL.Repo.ETS))
|
|
||||||
|> Text.encode_response()
|
|
||||||
|> HTTP.send(:found)
|
|
||||||
end
|
|
||||||
|
|
||||||
delete "/:id" do
|
|
||||||
conn
|
|
||||||
|> HTTP.wrap(id)
|
|
||||||
|> HTTP.handle(&URL.delete(&1, URL.Repo.ETS))
|
|
||||||
|> Text.encode_response()
|
|
||||||
|> HTTP.send(:ok)
|
|
||||||
end
|
|
||||||
|
|
||||||
match _ do
|
match _ do
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -3,15 +3,13 @@ defmodule Shortnr do
|
||||||
The Shortnr application entry point. Check README for usage documenation.
|
The Shortnr application entry point. Check README for usage documenation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Application
|
|
||||||
require Logger
|
require Logger
|
||||||
|
use Application
|
||||||
|
|
||||||
@impl true
|
@impl Application
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
port = port()
|
|
||||||
|
|
||||||
children = [
|
children = [
|
||||||
{Plug.Cowboy, scheme: :http, plug: Shortnr.Router, options: [port: port]}
|
{Plug.Cowboy, scheme: :http, plug: Shortnr.Router, options: [port: port()]}
|
||||||
]
|
]
|
||||||
|
|
||||||
if ets_implementation() == :dets do
|
if ets_implementation() == :dets do
|
||||||
|
@ -20,11 +18,11 @@ defmodule Shortnr do
|
||||||
:ets.new(:urls, [:set, :named_table])
|
:ets.new(:urls, [:set, :named_table])
|
||||||
end
|
end
|
||||||
|
|
||||||
Logger.info("server starting", port: port)
|
Logger.info("server starting", port: port())
|
||||||
Supervisor.start_link(children, strategy: :one_for_one)
|
Supervisor.start_link(children, strategy: :one_for_one)
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl Application
|
||||||
def stop(_state) do
|
def stop(_state) do
|
||||||
if ets_implementation() == :dets, do: :dets.close(:urls)
|
if ets_implementation() == :dets, do: :dets.close(:urls)
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,3 +64,12 @@ defmodule Shortnr.Transport.HTTP do
|
||||||
defp status_to_code(:not_found), do: 404
|
defp status_to_code(:not_found), do: 404
|
||||||
defp status_to_code(:internal_server_error), do: 500
|
defp status_to_code(:internal_server_error), do: 500
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule Shortnr.Transport.HTTP.Endpoints do
|
||||||
|
@moduledoc """
|
||||||
|
This modules is a behaviour that should be implemented by each service that needs to be routed by
|
||||||
|
Plug.Router
|
||||||
|
"""
|
||||||
|
|
||||||
|
@callback select(Plug.Conn.t(), atom, term) :: Plug.Conn.t()
|
||||||
|
end
|
||||||
|
|
|
@ -3,28 +3,29 @@ defmodule Shortnr.Transport.Text do
|
||||||
This modules contains functions to decode and encode text formatted http requests and responses.
|
This modules contains functions to decode and encode text formatted http requests and responses.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import Plug.Conn
|
|
||||||
alias Shortnr.Transport.HTTP
|
alias Shortnr.Transport.HTTP
|
||||||
alias Shortnr.Transport.Text.Encodable
|
alias Shortnr.Transport.Text.Encodable
|
||||||
|
|
||||||
@spec decode_request(Plug.Conn.t()) :: HTTP.ok_error()
|
import Plug.Conn
|
||||||
def decode_request(conn) do
|
|
||||||
|
@spec decode(HTTP.ok_error()) :: HTTP.ok_error()
|
||||||
|
def decode({:ok, _body, conn}) do
|
||||||
{:ok, body, conn} = read_body(conn)
|
{:ok, body, conn} = read_body(conn)
|
||||||
{:ok, body, conn}
|
{:ok, body, conn}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec encode_response(HTTP.ok_error()) :: HTTP.ok_error()
|
@spec encode(HTTP.ok_error()) :: HTTP.ok_error()
|
||||||
def encode_response(ok_error = {:error, _, _}), do: ok_error
|
def encode(ok_error = {:error, _, _}), do: ok_error
|
||||||
|
|
||||||
def encode_response({:ok, [], conn}) do
|
def encode({:ok, [], conn}) do
|
||||||
{:ok, "", conn}
|
{:ok, "", conn}
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode_response({:ok, body, conn}) when is_list(body) do
|
def encode({:ok, body, conn}) when is_list(body) do
|
||||||
{:ok, for(item <- body, into: "", do: "#{item}\n"), conn}
|
{:ok, for(item <- body, into: "", do: "#{item}\n"), conn}
|
||||||
end
|
end
|
||||||
|
|
||||||
def encode_response({:ok, body, conn}) do
|
def encode({:ok, body, conn}) do
|
||||||
{:ok, Encodable.encode(body), conn}
|
{:ok, Encodable.encode(body), conn}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
defmodule Shortnr.URL.Endpoints do
|
||||||
|
@moduledoc """
|
||||||
|
This module implements the Endpoints behaviour.
|
||||||
|
"""
|
||||||
|
alias Shortnr.Transport.{HTTP, Text}
|
||||||
|
alias Shortnr.Transport.HTTP.Endpoints
|
||||||
|
alias Shortnr.URL
|
||||||
|
|
||||||
|
@behaviour Endpoints
|
||||||
|
|
||||||
|
@impl Endpoints
|
||||||
|
def select(conn, name, arg \\ nil)
|
||||||
|
|
||||||
|
def select(conn, :list, _arg) do
|
||||||
|
conn
|
||||||
|
|> HTTP.wrap()
|
||||||
|
|> HTTP.handle(fn -> URL.list(URL.Repo.ETS) end)
|
||||||
|
|> Text.encode()
|
||||||
|
|> HTTP.send(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
def select(conn, :create, url) do
|
||||||
|
conn
|
||||||
|
|> HTTP.wrap(url)
|
||||||
|
|> HTTP.handle(&URL.create(&1, URL.Repo.ETS))
|
||||||
|
|> Text.encode()
|
||||||
|
|> HTTP.send(:created)
|
||||||
|
end
|
||||||
|
|
||||||
|
def select(conn, :get, id) do
|
||||||
|
conn
|
||||||
|
|> HTTP.wrap(id)
|
||||||
|
|> HTTP.handle(&URL.get(&1, URL.Repo.ETS))
|
||||||
|
|> Text.encode()
|
||||||
|
|> HTTP.send(:found)
|
||||||
|
end
|
||||||
|
|
||||||
|
def select(conn, :delete, id) do
|
||||||
|
conn
|
||||||
|
|> HTTP.wrap(id)
|
||||||
|
|> HTTP.handle(&URL.delete(&1, URL.Repo.ETS))
|
||||||
|
|> Text.encode()
|
||||||
|
|> HTTP.send(:ok)
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,10 +2,11 @@ defmodule Shortnr.URL.Repo.ETS do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module is an implemention of the Repo behaviour using the Erlang ETS library.
|
This module is an implemention of the Repo behaviour using the Erlang ETS library.
|
||||||
"""
|
"""
|
||||||
|
alias Shortnr.URL.Repo
|
||||||
|
|
||||||
@behaviour Shortnr.URL.Repo
|
@behaviour Repo
|
||||||
|
|
||||||
@impl true
|
@impl Repo
|
||||||
def get(key) do
|
def get(key) do
|
||||||
case ets().lookup(:urls, key) |> List.first() do
|
case ets().lookup(:urls, key) |> List.first() do
|
||||||
{_, url} -> {:ok, url}
|
{_, url} -> {:ok, url}
|
||||||
|
@ -13,25 +14,25 @@ defmodule Shortnr.URL.Repo.ETS do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl Repo
|
||||||
def put(url) do
|
def put(url) do
|
||||||
:ok = ets().insert(:urls, {url.id, url})
|
:ok = ets().insert(:urls, {url.id, url})
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl Repo
|
||||||
def list do
|
def list do
|
||||||
resp = ets().select(:urls, [{:"$1", [], [:"$1"]}])
|
resp = ets().select(:urls, [{:"$1", [], [:"$1"]}])
|
||||||
{:ok, resp |> Enum.map(&elem(&1, 1))}
|
{:ok, resp |> Enum.map(&elem(&1, 1))}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl Repo
|
||||||
def delete(key) do
|
def delete(key) do
|
||||||
:ok = ets().delete(:urls, key)
|
:ok = ets().delete(:urls, key)
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl Repo
|
||||||
def reset do
|
def reset do
|
||||||
:ok = ets().delete_all_objects(:urls)
|
:ok = ets().delete_all_objects(:urls)
|
||||||
end
|
end
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -8,6 +8,7 @@ defmodule Shortnr.MixProject do
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
elixir: "~> 1.9",
|
elixir: "~> 1.9",
|
||||||
start_permanent: Mix.env() == :prod,
|
start_permanent: Mix.env() == :prod,
|
||||||
|
dialyzer: [ignore_warnings: "dialyzer.ignore-warnings"],
|
||||||
deps: deps()
|
deps: deps()
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue