From e55aa4b85ce0738db1d5745391bdc6104dc81d05 Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Thu, 9 Sep 2021 17:26:40 +0200 Subject: [PATCH] Restructure code base for API requests/responses --- lib/architex/schema/server_key_info.ex | 2 +- .../{ => api_schemas}/client/request/ban.ex | 2 +- .../client/request/create_room.ex | 13 ++--- .../{ => api_schemas}/client/request/kick.ex | 2 +- .../{ => api_schemas}/client/request/login.ex | 9 ++-- .../client/request/messages.ex | 2 +- .../client/request/register.ex | 8 ++- .../{ => api_schemas}/client/request/sync.ex | 2 +- .../api_schemas/federation/request/profile.ex | 23 +++++++++ .../federation/response}/get_signing_keys.ex | 4 +- .../client/controllers/login_controller.ex | 44 +++++++--------- .../client/controllers/profile_controller.ex | 3 +- .../client/controllers/register_controller.ex | 51 +++++++++---------- .../client/controllers/room_controller.ex | 30 +++++------ .../controllers/query_controller.ex | 29 ++--------- lib/architex_web/federation/http_client.ex | 2 +- lib/architex_web/request.ex | 4 +- 17 files changed, 103 insertions(+), 127 deletions(-) rename lib/architex_web/{ => api_schemas}/client/request/ban.ex (92%) rename lib/architex_web/{ => api_schemas}/client/request/create_room.ex (82%) rename lib/architex_web/{ => api_schemas}/client/request/kick.ex (92%) rename lib/architex_web/{ => api_schemas}/client/request/login.ex (90%) rename lib/architex_web/{ => api_schemas}/client/request/messages.ex (96%) rename lib/architex_web/{ => api_schemas}/client/request/register.ex (91%) rename lib/architex_web/{ => api_schemas}/client/request/sync.ex (91%) create mode 100644 lib/architex_web/api_schemas/federation/request/profile.ex rename lib/architex_web/{federation/request => api_schemas/federation/response}/get_signing_keys.ex (93%) diff --git a/lib/architex/schema/server_key_info.ex b/lib/architex/schema/server_key_info.ex index ba8aa3b..8de790a 100644 --- a/lib/architex/schema/server_key_info.ex +++ b/lib/architex/schema/server_key_info.ex @@ -5,7 +5,7 @@ defmodule Architex.ServerKeyInfo do alias Architex.{Repo, ServerKeyInfo, SigningKey} alias ArchitexWeb.Federation.HTTPClient - alias ArchitexWeb.Federation.Request.GetSigningKeys + alias ArchitexWeb.Federation.Response.GetSigningKeys alias Ecto.Multi @primary_key {:server_name, :string, []} diff --git a/lib/architex_web/client/request/ban.ex b/lib/architex_web/api_schemas/client/request/ban.ex similarity index 92% rename from lib/architex_web/client/request/ban.ex rename to lib/architex_web/api_schemas/client/request/ban.ex index ee7f4c7..694667f 100644 --- a/lib/architex_web/client/request/ban.ex +++ b/lib/architex_web/api_schemas/client/request/ban.ex @@ -1,5 +1,5 @@ defmodule ArchitexWeb.Client.Request.Ban do - use ArchitexWeb.Request + use ArchitexWeb.APIRequest @type t :: %__MODULE__{ user_id: String.t(), diff --git a/lib/architex_web/client/request/create_room.ex b/lib/architex_web/api_schemas/client/request/create_room.ex similarity index 82% rename from lib/architex_web/client/request/create_room.ex rename to lib/architex_web/api_schemas/client/request/create_room.ex index 3d7d78f..77d72c7 100644 --- a/lib/architex_web/client/request/create_room.ex +++ b/lib/architex_web/api_schemas/client/request/create_room.ex @@ -1,9 +1,5 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do - use Ecto.Schema - - import Ecto.Changeset - - alias Ecto.Changeset + use ArchitexWeb.APIRequest @type t :: %__MODULE__{ visibility: String.t() | nil, @@ -29,8 +25,8 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do # is_direct, power_level_content_override end - def changeset(params) do - %__MODULE__{} + def changeset(data, params) do + data |> cast(params, [ :visibility, :room_alias_name, @@ -42,7 +38,4 @@ defmodule ArchitexWeb.Client.Request.CreateRoom do ]) |> validate_inclusion(:preset, ["private_chat", "public_chat", "trusted_private_chat"]) end - - def get_error(%Changeset{errors: [error | _]}), do: get_error(error) - def get_error(_), do: :bad_json end diff --git a/lib/architex_web/client/request/kick.ex b/lib/architex_web/api_schemas/client/request/kick.ex similarity index 92% rename from lib/architex_web/client/request/kick.ex rename to lib/architex_web/api_schemas/client/request/kick.ex index 5adf307..0d2bc81 100644 --- a/lib/architex_web/client/request/kick.ex +++ b/lib/architex_web/api_schemas/client/request/kick.ex @@ -1,5 +1,5 @@ defmodule ArchitexWeb.Client.Request.Kick do - use ArchitexWeb.Request + use ArchitexWeb.APIRequest @type t :: %__MODULE__{ user_id: String.t(), diff --git a/lib/architex_web/client/request/login.ex b/lib/architex_web/api_schemas/client/request/login.ex similarity index 90% rename from lib/architex_web/client/request/login.ex rename to lib/architex_web/api_schemas/client/request/login.ex index 915d17e..8f5e1d5 100644 --- a/lib/architex_web/client/request/login.ex +++ b/lib/architex_web/api_schemas/client/request/login.ex @@ -1,8 +1,7 @@ defmodule ArchitexWeb.Client.Request.Login do - use Ecto.Schema - - import Ecto.Changeset + use ArchitexWeb.APIRequest + # TODO: Identifier @type t :: %__MODULE__{ type: String.t(), password: String.t(), @@ -23,8 +22,8 @@ defmodule ArchitexWeb.Client.Request.Login do end end - def changeset(params) do - %__MODULE__{} + def changeset(data, params) do + data |> cast(params, [:type, :password, :device_id, :initial_device_display_name]) |> cast_embed(:identifier, with: &identifier_changeset/2, required: true) |> validate_required([:type, :password]) diff --git a/lib/architex_web/client/request/messages.ex b/lib/architex_web/api_schemas/client/request/messages.ex similarity index 96% rename from lib/architex_web/client/request/messages.ex rename to lib/architex_web/api_schemas/client/request/messages.ex index 346e14d..f2cf690 100644 --- a/lib/architex_web/client/request/messages.ex +++ b/lib/architex_web/api_schemas/client/request/messages.ex @@ -1,5 +1,5 @@ defmodule ArchitexWeb.Client.Request.Messages do - use ArchitexWeb.Request + use ArchitexWeb.APIRequest @type t :: %__MODULE__{ from: String.t(), diff --git a/lib/architex_web/client/request/register.ex b/lib/architex_web/api_schemas/client/request/register.ex similarity index 91% rename from lib/architex_web/client/request/register.ex rename to lib/architex_web/api_schemas/client/request/register.ex index edf561b..1532706 100644 --- a/lib/architex_web/client/request/register.ex +++ b/lib/architex_web/api_schemas/client/request/register.ex @@ -1,7 +1,5 @@ defmodule ArchitexWeb.Client.Request.Register do - use Ecto.Schema - - import Ecto.Changeset + use ArchitexWeb.APIRequest alias Ecto.Changeset @@ -22,8 +20,8 @@ defmodule ArchitexWeb.Client.Request.Register do field :inhibit_login, :boolean, default: false end - def changeset(params) do - %__MODULE__{} + def changeset(data, params) do + data |> cast(params, [ :device_id, :initial_device_display_name, diff --git a/lib/architex_web/client/request/sync.ex b/lib/architex_web/api_schemas/client/request/sync.ex similarity index 91% rename from lib/architex_web/client/request/sync.ex rename to lib/architex_web/api_schemas/client/request/sync.ex index f73bd4f..c324895 100644 --- a/lib/architex_web/client/request/sync.ex +++ b/lib/architex_web/api_schemas/client/request/sync.ex @@ -1,5 +1,5 @@ defmodule ArchitexWeb.Client.Request.Sync do - use ArchitexWeb.Request + use ArchitexWeb.APIRequest @type t :: %__MODULE__{} diff --git a/lib/architex_web/api_schemas/federation/request/profile.ex b/lib/architex_web/api_schemas/federation/request/profile.ex new file mode 100644 index 0000000..e98a85e --- /dev/null +++ b/lib/architex_web/api_schemas/federation/request/profile.ex @@ -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 diff --git a/lib/architex_web/federation/request/get_signing_keys.ex b/lib/architex_web/api_schemas/federation/response/get_signing_keys.ex similarity index 93% rename from lib/architex_web/federation/request/get_signing_keys.ex rename to lib/architex_web/api_schemas/federation/response/get_signing_keys.ex index be65e29..3b69336 100644 --- a/lib/architex_web/federation/request/get_signing_keys.ex +++ b/lib/architex_web/api_schemas/federation/response/get_signing_keys.ex @@ -1,5 +1,5 @@ -defmodule ArchitexWeb.Federation.Request.GetSigningKeys do - use ArchitexWeb.Request +defmodule ArchitexWeb.Federation.Response.GetSigningKeys do + use ArchitexWeb.APIRequest @type t :: %__MODULE__{ server_name: String.t(), diff --git a/lib/architex_web/client/controllers/login_controller.ex b/lib/architex_web/client/controllers/login_controller.ex index a9ab1b3..4842665 100644 --- a/lib/architex_web/client/controllers/login_controller.ex +++ b/lib/architex_web/client/controllers/login_controller.ex @@ -2,11 +2,9 @@ defmodule ArchitexWeb.Client.LoginController do use ArchitexWeb, :controller import ArchitexWeb.Error - import Ecto.Changeset alias Architex.{Repo, Account, Device} alias ArchitexWeb.Client.Request.Login - alias Ecto.Changeset @login_type "m.login.password" @@ -33,32 +31,28 @@ defmodule ArchitexWeb.Client.LoginController do conn, %{"type" => @login_type, "identifier" => %{"type" => "m.id.user"}} = params ) do - case Login.changeset(params) do - %Changeset{valid?: true} = cs -> - input = apply_changes(cs) + with {:ok, request} <- Login.parse(params) do + case Account.login(request) |> Repo.transaction() do + {: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 - {: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 - } + conn + |> put_status(200) + |> json(data) - conn - |> put_status(200) - |> json(data) + {:error, error} when is_atom(error) -> + put_error(conn, error) - {:error, error} when is_atom(error) -> - put_error(conn, error) - - {:error, _} -> - put_error(conn, :unknown) - end - - _ -> - put_error(conn, :bad_json) + {:error, _} -> + put_error(conn, :unknown) + end + else + _ -> put_error(conn, :bad_json) end end diff --git a/lib/architex_web/client/controllers/profile_controller.ex b/lib/architex_web/client/controllers/profile_controller.ex index 153d66a..cfadc28 100644 --- a/lib/architex_web/client/controllers/profile_controller.ex +++ b/lib/architex_web/client/controllers/profile_controller.ex @@ -85,7 +85,8 @@ defmodule ArchitexWeb.Client.ProfileController do put_error(conn, :not_found, "User was not found.") end 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} -> conn |> put_status(200) diff --git a/lib/architex_web/client/controllers/register_controller.ex b/lib/architex_web/client/controllers/register_controller.ex index 0b24d7b..794e5ab 100644 --- a/lib/architex_web/client/controllers/register_controller.ex +++ b/lib/architex_web/client/controllers/register_controller.ex @@ -2,11 +2,9 @@ defmodule ArchitexWeb.Client.RegisterController do use ArchitexWeb, :controller import ArchitexWeb.Error - import Ecto.Changeset alias Architex.{Repo, Account, Device} alias ArchitexWeb.Client.Request.Register - alias Ecto.Changeset @register_type "m.login.dummy" @@ -16,35 +14,32 @@ defmodule ArchitexWeb.Client.RegisterController do Action for POST /_matrix/client/r0/register. """ def register(conn, %{"auth" => %{"type" => @register_type}} = params) do - case Register.changeset(params) do - %Changeset{valid?: true} = cs -> - %Register{inhibit_login: inhibit_login} = input = apply_changes(cs) + with {:ok, %Register{inhibit_login: inhibit_login} = request} <- Register.parse(params) do + case Account.register(request) |> Repo.transaction() do + {: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 - {:ok, - %{ - account: %Account{localpart: localpart}, - device: %Device{id: device_id, access_token: access_token} - }} -> - data = %{user_id: Architex.get_mxid(localpart)} + data = + if not inhibit_login do + data + |> Map.put(:device_id, device_id) + |> Map.put(:access_token, access_token) + else + data + end - data = - if not inhibit_login do - 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 + conn + |> put_status(200) + |> json(data) + {:error, _, cs, _} -> + put_error(conn, Register.get_error(cs)) + end + else _ -> put_error(conn, :bad_json) end diff --git a/lib/architex_web/client/controllers/room_controller.ex b/lib/architex_web/client/controllers/room_controller.ex index 207c531..4869561 100644 --- a/lib/architex_web/client/controllers/room_controller.ex +++ b/lib/architex_web/client/controllers/room_controller.ex @@ -2,12 +2,11 @@ defmodule ArchitexWeb.Client.RoomController do use ArchitexWeb, :controller import ArchitexWeb.Error - import Ecto.{Changeset, Query} + import Ecto.Query alias Architex.{Repo, Room, RoomServer, Event} alias Architex.Types.UserId alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban, Messages} - alias Ecto.Changeset alias Plug.Conn @doc """ @@ -16,23 +15,20 @@ defmodule ArchitexWeb.Client.RoomController do Action for POST /_matrix/client/r0/createRoom. """ def create(%Conn{assigns: %{account: account}} = conn, params) do - case CreateRoom.changeset(params) do - %Changeset{valid?: true} = cs -> - input = apply_changes(cs) + with {:ok, request} <- CreateRoom.parse(params) do + case Room.create(account, request) do + {:ok, room_id} -> + conn + |> put_status(200) + |> json(%{room_id: room_id}) - case Room.create(account, input) do - {:ok, room_id} -> - 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, :authorization} -> + put_error(conn, :invalid_room_state) + {:error, :unknown} -> + put_error(conn, :unknown) + end + else _ -> put_error(conn, :bad_json) end diff --git a/lib/architex_web/federation/controllers/query_controller.ex b/lib/architex_web/federation/controllers/query_controller.ex index 3468f02..7360f9c 100644 --- a/lib/architex_web/federation/controllers/query_controller.ex +++ b/lib/architex_web/federation/controllers/query_controller.ex @@ -7,29 +7,7 @@ defmodule ArchitexWeb.Federation.QueryController do alias Architex.{Repo, Account} alias Architex.Types.UserId - - 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 + alias ArchitexWeb.Federation.Request.Profile @doc """ 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. """ def profile(conn, params) do - with {:ok, - %ProfileRequest{user_id: %UserId{localpart: localpart, domain: domain}, field: field}} <- - ProfileRequest.validate(params) do + with {:ok, %Profile{user_id: %UserId{localpart: localpart, domain: domain}, field: field}} <- + Profile.parse(params) do if domain == Architex.server_name() do case Repo.one(from a in Account, where: a.localpart == ^localpart) do %Account{displayname: displayname, avatar_url: avatar_url} -> diff --git a/lib/architex_web/federation/http_client.ex b/lib/architex_web/federation/http_client.ex index 9e14261..b77c7a2 100644 --- a/lib/architex_web/federation/http_client.ex +++ b/lib/architex_web/federation/http_client.ex @@ -7,7 +7,7 @@ defmodule ArchitexWeb.Federation.HTTPClient do use Tesla alias ArchitexWeb.Endpoint - alias ArchitexWeb.Federation.Request.GetSigningKeys + alias ArchitexWeb.Federation.Response.GetSigningKeys alias ArchitexWeb.Federation.Middleware.SignRequest alias ArchitexWeb.Router.Helpers, as: RouteHelpers diff --git a/lib/architex_web/request.ex b/lib/architex_web/request.ex index 5283faf..fae272a 100644 --- a/lib/architex_web/request.ex +++ b/lib/architex_web/request.ex @@ -1,4 +1,4 @@ -defmodule ArchitexWeb.Request do +defmodule ArchitexWeb.APIRequest do import Ecto.Changeset alias Ecto.Changeset @@ -19,7 +19,7 @@ defmodule ArchitexWeb.Request do @spec parse(map()) :: {:ok, struct()} | {:error, Changeset.t()} def parse(params) do - ArchitexWeb.Request.parse(__MODULE__, params) + ArchitexWeb.APIRequest.parse(__MODULE__, params) end end end