Add initial implementatin of client /sync endpoint without 'since'

parameter
This commit is contained in:
Pim Kunis 2021-09-07 20:24:06 +02:00
parent 5f0fd7fabe
commit 7a1bf01869
7 changed files with 121 additions and 12 deletions

View file

@ -4,8 +4,8 @@ defmodule Architex.Event.Formatters do
"""
alias Architex.Event
@spec for_client(Event.t()) :: map()
def for_client(%Event{
@spec messages_response(Event.t()) :: map()
def messages_response(%Event{
content: content,
type: type,
id: event_id,
@ -30,6 +30,27 @@ defmodule Architex.Event.Formatters do
data
end
def sync_response(%Event{
content: content,
type: type,
id: event_id,
sender: sender,
origin_server_ts: origin_server_ts,
unsigned: unsigned
}) do
data = %{
content: content,
type: type,
event_id: event_id,
sender: to_string(sender),
origin_server_ts: origin_server_ts
}
data = if unsigned, do: Map.put(data, :unsigned, unsigned), else: data
data
end
@spec as_pdu(Event.t()) :: map()
def as_pdu(%Event{
auth_events: auth_events,

View file

@ -70,8 +70,6 @@ defmodule Architex.Room do
@spec get_messages(Room.t(), Messages.t()) :: {[Event.t()], integer() | nil, integer() | nil}
def get_messages(room, %Messages{from: from, to: to, dir: dir, limit: limit}) do
limit = limit || 10
events =
room
|> Ecto.assoc(:events)
@ -90,6 +88,8 @@ defmodule Architex.Room do
# When 'from' is empty, we return events from the start or end
# of the room's history.
# TODO: Might actually not be needed, could be that Quaternion is passing
# an empty string because the /sync call fails.
@spec events_from(Ecto.Query.t(), String.t(), String.t()) :: Ecto.Query.t()
defp events_from(query, "", _), do: query

View file

@ -249,7 +249,7 @@ defmodule ArchitexWeb.Client.RoomController do
case Repo.one(room_query) do
%Room{} = room ->
{events, start, end_} = Room.get_messages(room, request)
events = Enum.map(events, &Event.Formatters.for_client/1)
events = Enum.map(events, &Event.Formatters.messages_response/1)
data = %{chunk: events}
data = if start, do: Map.put(data, :start, Integer.to_string(start)), else: data
data = if end_, do: Map.put(data, :end, Integer.to_string(end_)), else: data

View file

@ -0,0 +1,73 @@
defmodule ArchitexWeb.Client.SyncController do
use ArchitexWeb, :controller
import ArchitexWeb.Error
import Ecto.Query
alias Architex.{Repo, Event, Account, Room, JoinedRoom}
alias Plug.Conn
@doc """
Synchronise the client's state with the latest state on the server.
Parameters: %{"filter" => "{\"account_data\":{},\"presence\":{},\"room\":{\"account_data\":{},\"ephemeral\":{},\"state\":{\"lazy_load_members\":true},\"timeline\":{\"limit\":100}}}", "path" => ["_matrix", "client", "r0", "sync"], "timeout" => "30000"}
Action for GET /_matrix/client/r0/sync.
"""
# When no "since" is specified, return the most recent messages.
def sync(%Conn{assigns: %{account: %Account{id: account_id}}} = conn, params)
when not is_map_key(params, "since") do
# joined_rooms =
# account
# |> Ecto.assoc(:joined_rooms)
# |> Repo.all()
# |> Enum.into(%{}, fn %Room{id: room_id} = room ->
# {room_id, room}
# end)
events_per_room =
Event
|> join(:inner, [e], jr in JoinedRoom,
on: jr.room_id == e.room_id and jr.account_id == ^account_id
)
|> join(:inner, [e, jr], r in Room, on: r.id == jr.room_id)
|> order_by(asc: :origin_server_ts, asc: :nid)
|> Repo.all()
|> Enum.group_by(& &1.room_id)
join =
Enum.into(events_per_room, %{}, fn {room_id, [%Event{nid: first_nid} | _] = events} ->
joined_room = %{
timeline: %{
events: Enum.map(events, &Event.Formatters.sync_response/1),
limited: false,
prev_batch: Integer.to_string(first_nid)
}
}
{room_id, joined_room}
end)
next_batch = Enum.map(events_per_room, fn {_, events} ->
%Event{nid: last_nid} = List.last(events)
last_nid
end)
|> Enum.max(fn -> 0 end)
data = %{
next_batch: Integer.to_string(next_batch),
rooms: %{
join: join,
invite: %{},
leave: %{}
}
}
conn
|> put_status(200)
|> json(data)
end
# TODO: Long-poll for new incoming events.
# Should think about how to implement this in a nice way.
def sync(conn, _params), do: put_error(conn, :unknown, "Not implemented yet.")
end

View file

@ -2,19 +2,19 @@ defmodule ArchitexWeb.Client.Request.Messages do
use ArchitexWeb.Request
@type t :: %__MODULE__{
from: String.t(),
to: String.t() | nil,
dir: String.t(),
limit: integer() | nil,
filter: String.t() | nil
}
from: String.t(),
to: String.t() | nil,
dir: String.t(),
limit: integer() | nil,
filter: String.t() | nil
}
@primary_key false
embedded_schema do
field :from, :string
field :to, :string
field :dir, :string
field :limit, :integer
field :limit, :integer, default: 10
field :filter, :string
end

View file

@ -0,0 +1,14 @@
defmodule ArchitexWeb.Client.Request.Sync do
use ArchitexWeb.Request
@type t :: %__MODULE__{}
@primary_key false
embedded_schema do
field :filter, :string
field :since, :string
field :full_state, :boolean, default: false
field :set_presence, :string, default: "online"
field :timeout, :integer, default: 0
end
end

View file

@ -54,6 +54,7 @@ defmodule ArchitexWeb.Router do
post "/createRoom", RoomController, :create
get "/joined_rooms", RoomController, :joined_rooms
get "/capabilities", InfoController, :capabilities
get "/sync", SyncController, :sync
scope "/directory" do
put "/room/:alias", AliasesController, :create