Restructure code base for API requests/responses

This commit is contained in:
Pim Kunis 2021-09-09 17:26:40 +02:00
parent b60c80b882
commit e55aa4b85c
17 changed files with 103 additions and 127 deletions

View file

@ -5,7 +5,7 @@ defmodule Architex.ServerKeyInfo do
alias Architex.{Repo, ServerKeyInfo, SigningKey} alias Architex.{Repo, ServerKeyInfo, SigningKey}
alias ArchitexWeb.Federation.HTTPClient alias ArchitexWeb.Federation.HTTPClient
alias ArchitexWeb.Federation.Request.GetSigningKeys alias ArchitexWeb.Federation.Response.GetSigningKeys
alias Ecto.Multi alias Ecto.Multi
@primary_key {:server_name, :string, []} @primary_key {:server_name, :string, []}

View file

@ -1,5 +1,5 @@
defmodule ArchitexWeb.Client.Request.Ban do defmodule ArchitexWeb.Client.Request.Ban do
use ArchitexWeb.Request use ArchitexWeb.APIRequest
@type t :: %__MODULE__{ @type t :: %__MODULE__{
user_id: String.t(), user_id: String.t(),

View file

@ -1,9 +1,5 @@
defmodule ArchitexWeb.Client.Request.CreateRoom do defmodule ArchitexWeb.Client.Request.CreateRoom do
use Ecto.Schema use ArchitexWeb.APIRequest
import Ecto.Changeset
alias Ecto.Changeset
@type t :: %__MODULE__{ @type t :: %__MODULE__{
visibility: String.t() | nil, visibility: String.t() | nil,
@ -29,8 +25,8 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do
# is_direct, power_level_content_override # is_direct, power_level_content_override
end end
def changeset(params) do def changeset(data, params) do
%__MODULE__{} data
|> cast(params, [ |> cast(params, [
:visibility, :visibility,
:room_alias_name, :room_alias_name,
@ -42,7 +38,4 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do
]) ])
|> validate_inclusion(:preset, ["private_chat", "public_chat", "trusted_private_chat"]) |> validate_inclusion(:preset, ["private_chat", "public_chat", "trusted_private_chat"])
end end
def get_error(%Changeset{errors: [error | _]}), do: get_error(error)
def get_error(_), do: :bad_json
end end

View file

@ -1,5 +1,5 @@
defmodule ArchitexWeb.Client.Request.Kick do defmodule ArchitexWeb.Client.Request.Kick do
use ArchitexWeb.Request use ArchitexWeb.APIRequest
@type t :: %__MODULE__{ @type t :: %__MODULE__{
user_id: String.t(), user_id: String.t(),

View file

@ -1,8 +1,7 @@
defmodule ArchitexWeb.Client.Request.Login do defmodule ArchitexWeb.Client.Request.Login do
use Ecto.Schema use ArchitexWeb.APIRequest
import Ecto.Changeset
# TODO: Identifier
@type t :: %__MODULE__{ @type t :: %__MODULE__{
type: String.t(), type: String.t(),
password: String.t(), password: String.t(),
@ -23,8 +22,8 @@ defmodule ArchitexWeb.Client.Request.Login do
end end
end end
def changeset(params) do def changeset(data, params) do
%__MODULE__{} data
|> cast(params, [:type, :password, :device_id, :initial_device_display_name]) |> cast(params, [:type, :password, :device_id, :initial_device_display_name])
|> cast_embed(:identifier, with: &identifier_changeset/2, required: true) |> cast_embed(:identifier, with: &identifier_changeset/2, required: true)
|> validate_required([:type, :password]) |> validate_required([:type, :password])

View file

@ -1,5 +1,5 @@
defmodule ArchitexWeb.Client.Request.Messages do defmodule ArchitexWeb.Client.Request.Messages do
use ArchitexWeb.Request use ArchitexWeb.APIRequest
@type t :: %__MODULE__{ @type t :: %__MODULE__{
from: String.t(), from: String.t(),

View file

@ -1,7 +1,5 @@
defmodule ArchitexWeb.Client.Request.Register do defmodule ArchitexWeb.Client.Request.Register do
use Ecto.Schema use ArchitexWeb.APIRequest
import Ecto.Changeset
alias Ecto.Changeset alias Ecto.Changeset
@ -22,8 +20,8 @@ defmodule ArchitexWeb.Client.Request.Register do
field :inhibit_login, :boolean, default: false field :inhibit_login, :boolean, default: false
end end
def changeset(params) do def changeset(data, params) do
%__MODULE__{} data
|> cast(params, [ |> cast(params, [
:device_id, :device_id,
:initial_device_display_name, :initial_device_display_name,

View file

@ -1,5 +1,5 @@
defmodule ArchitexWeb.Client.Request.Sync do defmodule ArchitexWeb.Client.Request.Sync do
use ArchitexWeb.Request use ArchitexWeb.APIRequest
@type t :: %__MODULE__{} @type t :: %__MODULE__{}

View file

@ -0,0 +1,23 @@
defmodule ArchitexWeb.Federation.Request.Profile do
use ArchitexWeb.APIRequest
alias Architex.Types.UserId
@type t :: %__MODULE__{
user_id: UserId.t(),
field: String.t() | nil
}
@primary_key false
embedded_schema do
field :user_id, UserId
field :field, :string
end
def changeset(data, params) do
data
|> cast(params, [:user_id, :field])
|> validate_required([:user_id])
|> validate_inclusion(:field, ["displayname", "avatar_url"])
end
end

View file

@ -1,5 +1,5 @@
defmodule ArchitexWeb.Federation.Request.GetSigningKeys do defmodule ArchitexWeb.Federation.Response.GetSigningKeys do
use ArchitexWeb.Request use ArchitexWeb.APIRequest
@type t :: %__MODULE__{ @type t :: %__MODULE__{
server_name: String.t(), server_name: String.t(),

View file

@ -2,11 +2,9 @@ defmodule ArchitexWeb.Client.LoginController do
use ArchitexWeb, :controller use ArchitexWeb, :controller
import ArchitexWeb.Error import ArchitexWeb.Error
import Ecto.Changeset
alias Architex.{Repo, Account, Device} alias Architex.{Repo, Account, Device}
alias ArchitexWeb.Client.Request.Login alias ArchitexWeb.Client.Request.Login
alias Ecto.Changeset
@login_type "m.login.password" @login_type "m.login.password"
@ -33,32 +31,28 @@ defmodule ArchitexWeb.Client.LoginController do
conn, conn,
%{"type" => @login_type, "identifier" => %{"type" => "m.id.user"}} = params %{"type" => @login_type, "identifier" => %{"type" => "m.id.user"}} = params
) do ) do
case Login.changeset(params) do with {:ok, request} <- Login.parse(params) do
%Changeset{valid?: true} = cs -> case Account.login(request) |> Repo.transaction() do
input = apply_changes(cs) {:ok,
{%Account{localpart: localpart}, %Device{access_token: access_token, id: device_id}}} ->
data = %{
user_id: Architex.get_mxid(localpart),
access_token: access_token,
device_id: device_id
}
case Account.login(input) |> Repo.transaction() do conn
{:ok, |> put_status(200)
{%Account{localpart: localpart}, %Device{access_token: access_token, id: device_id}}} -> |> json(data)
data = %{
user_id: Architex.get_mxid(localpart),
access_token: access_token,
device_id: device_id
}
conn {:error, error} when is_atom(error) ->
|> put_status(200) put_error(conn, error)
|> json(data)
{:error, error} when is_atom(error) -> {:error, _} ->
put_error(conn, error) put_error(conn, :unknown)
end
{:error, _} -> else
put_error(conn, :unknown) _ -> put_error(conn, :bad_json)
end
_ ->
put_error(conn, :bad_json)
end end
end end

View file

@ -85,7 +85,8 @@ defmodule ArchitexWeb.Client.ProfileController do
put_error(conn, :not_found, "User was not found.") put_error(conn, :not_found, "User was not found.")
end end
else else
case HTTPClient.client(domain) |> HTTPClient.query_profile(user_id, Atom.to_string(property_key)) do case HTTPClient.client(domain)
|> HTTPClient.query_profile(user_id, Atom.to_string(property_key)) do
{:ok, response} -> {:ok, response} ->
conn conn
|> put_status(200) |> put_status(200)

View file

@ -2,11 +2,9 @@ defmodule ArchitexWeb.Client.RegisterController do
use ArchitexWeb, :controller use ArchitexWeb, :controller
import ArchitexWeb.Error import ArchitexWeb.Error
import Ecto.Changeset
alias Architex.{Repo, Account, Device} alias Architex.{Repo, Account, Device}
alias ArchitexWeb.Client.Request.Register alias ArchitexWeb.Client.Request.Register
alias Ecto.Changeset
@register_type "m.login.dummy" @register_type "m.login.dummy"
@ -16,35 +14,32 @@ defmodule ArchitexWeb.Client.RegisterController do
Action for POST /_matrix/client/r0/register. Action for POST /_matrix/client/r0/register.
""" """
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
case Register.changeset(params) do with {:ok, %Register{inhibit_login: inhibit_login} = request} <- Register.parse(params) do
%Changeset{valid?: true} = cs -> case Account.register(request) |> Repo.transaction() do
%Register{inhibit_login: inhibit_login} = input = apply_changes(cs) {:ok,
%{
account: %Account{localpart: localpart},
device: %Device{id: device_id, access_token: access_token}
}} ->
data = %{user_id: Architex.get_mxid(localpart)}
case Account.register(input) |> Repo.transaction() do data =
{:ok, if not inhibit_login do
%{ data
account: %Account{localpart: localpart}, |> Map.put(:device_id, device_id)
device: %Device{id: device_id, access_token: access_token} |> Map.put(:access_token, access_token)
}} -> else
data = %{user_id: Architex.get_mxid(localpart)} data
end
data = conn
if not inhibit_login do |> put_status(200)
data |> json(data)
|> Map.put(:device_id, device_id)
|> Map.put(:access_token, access_token)
else
data
end
conn
|> put_status(200)
|> json(data)
{:error, _, cs, _} ->
put_error(conn, Register.get_error(cs))
end
{:error, _, cs, _} ->
put_error(conn, Register.get_error(cs))
end
else
_ -> _ ->
put_error(conn, :bad_json) put_error(conn, :bad_json)
end end

View file

@ -2,12 +2,11 @@ defmodule ArchitexWeb.Client.RoomController do
use ArchitexWeb, :controller use ArchitexWeb, :controller
import ArchitexWeb.Error import ArchitexWeb.Error
import Ecto.{Changeset, Query} import Ecto.Query
alias Architex.{Repo, Room, RoomServer, Event} alias Architex.{Repo, Room, RoomServer, Event}
alias Architex.Types.UserId alias Architex.Types.UserId
alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban, Messages} alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban, Messages}
alias Ecto.Changeset
alias Plug.Conn alias Plug.Conn
@doc """ @doc """
@ -16,23 +15,20 @@ defmodule ArchitexWeb.Client.RoomController do
Action for POST /_matrix/client/r0/createRoom. Action for POST /_matrix/client/r0/createRoom.
""" """
def create(%Conn{assigns: %{account: account}} = conn, params) do def create(%Conn{assigns: %{account: account}} = conn, params) do
case CreateRoom.changeset(params) do with {:ok, request} <- CreateRoom.parse(params) do
%Changeset{valid?: true} = cs -> case Room.create(account, request) do
input = apply_changes(cs) {:ok, room_id} ->
conn
|> put_status(200)
|> json(%{room_id: room_id})
case Room.create(account, input) do {:error, :authorization} ->
{:ok, room_id} -> put_error(conn, :invalid_room_state)
conn
|> put_status(200)
|> json(%{room_id: room_id})
{:error, :authorization} ->
put_error(conn, :invalid_room_state)
{:error, :unknown} ->
put_error(conn, :unknown)
end
{:error, :unknown} ->
put_error(conn, :unknown)
end
else
_ -> _ ->
put_error(conn, :bad_json) put_error(conn, :bad_json)
end end

View file

@ -7,29 +7,7 @@ defmodule ArchitexWeb.Federation.QueryController do
alias Architex.{Repo, Account} alias Architex.{Repo, Account}
alias Architex.Types.UserId alias Architex.Types.UserId
alias ArchitexWeb.Federation.Request.Profile
defmodule ProfileRequest do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :user_id, UserId
field :field, :string
end
def validate(params) do
%__MODULE__{}
|> cast(params, [:user_id, :field])
|> validate_required([:user_id])
|> validate_inclusion(:field, ["displayname", "avatar_url"])
|> then(fn
%Ecto.Changeset{valid?: true} = cs -> {:ok, apply_changes(cs)}
_ -> :error
end)
end
end
@doc """ @doc """
Performs a query to get profile information, such as a display name or avatar, Performs a query to get profile information, such as a display name or avatar,
@ -38,9 +16,8 @@ defmodule ArchitexWeb.Federation.QueryController do
Action for GET /_matrix/federation/v1/query/profile. Action for GET /_matrix/federation/v1/query/profile.
""" """
def profile(conn, params) do def profile(conn, params) do
with {:ok, with {:ok, %Profile{user_id: %UserId{localpart: localpart, domain: domain}, field: field}} <-
%ProfileRequest{user_id: %UserId{localpart: localpart, domain: domain}, field: field}} <- Profile.parse(params) do
ProfileRequest.validate(params) do
if domain == Architex.server_name() do if domain == Architex.server_name() do
case Repo.one(from a in Account, where: a.localpart == ^localpart) do case Repo.one(from a in Account, where: a.localpart == ^localpart) do
%Account{displayname: displayname, avatar_url: avatar_url} -> %Account{displayname: displayname, avatar_url: avatar_url} ->

View file

@ -7,7 +7,7 @@ defmodule ArchitexWeb.Federation.HTTPClient do
use Tesla use Tesla
alias ArchitexWeb.Endpoint alias ArchitexWeb.Endpoint
alias ArchitexWeb.Federation.Request.GetSigningKeys alias ArchitexWeb.Federation.Response.GetSigningKeys
alias ArchitexWeb.Federation.Middleware.SignRequest alias ArchitexWeb.Federation.Middleware.SignRequest
alias ArchitexWeb.Router.Helpers, as: RouteHelpers alias ArchitexWeb.Router.Helpers, as: RouteHelpers

View file

@ -1,4 +1,4 @@
defmodule ArchitexWeb.Request do defmodule ArchitexWeb.APIRequest do
import Ecto.Changeset import Ecto.Changeset
alias Ecto.Changeset alias Ecto.Changeset
@ -19,7 +19,7 @@ defmodule ArchitexWeb.Request do
@spec parse(map()) :: {:ok, struct()} | {:error, Changeset.t()} @spec parse(map()) :: {:ok, struct()} | {:error, Changeset.t()}
def parse(params) do def parse(params) do
ArchitexWeb.Request.parse(__MODULE__, params) ArchitexWeb.APIRequest.parse(__MODULE__, params)
end end
end end
end end