added paginate links to headers for /chats/:id/messages
authorMaksim Pechnikov <parallel588@gmail.com>
Thu, 10 Sep 2020 09:24:44 +0000 (12:24 +0300)
committerMaksim Pechnikov <parallel588@gmail.com>
Thu, 10 Sep 2020 09:39:29 +0000 (12:39 +0300)
lib/pleroma/chat.ex
lib/pleroma/web/api_spec/operations/chat_operation.ex
lib/pleroma/web/controller_helper.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
test/web/pleroma_api/controllers/chat_controller_test.exs

index 24a86371e7801594caa18acc33b7b9eb999f73b6..202fffb8adf0d968271fe4401e035b464c06b5bc 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.Chat do
   use Ecto.Schema
 
   import Ecto.Changeset
+  import Ecto.Query
 
   alias Pleroma.Repo
   alias Pleroma.User
@@ -16,6 +17,7 @@ defmodule Pleroma.Chat do
   It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
   """
 
+  @type t :: %__MODULE__{}
   @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
 
   schema "chats" do
@@ -39,16 +41,28 @@ defmodule Pleroma.Chat do
     |> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
   end
 
+  @spec get_by_user_and_id(User.t(), FlakeId.Ecto.CompatType.t()) ::
+          {:ok, t()} | {:error, :not_found}
+  def get_by_user_and_id(%User{id: user_id}, id) do
+    from(c in __MODULE__,
+      where: c.id == ^id,
+      where: c.user_id == ^user_id
+    )
+    |> Repo.find_resource()
+  end
+
+  @spec get_by_id(FlakeId.Ecto.CompatType.t()) :: t() | nil
   def get_by_id(id) do
-    __MODULE__
-    |> Repo.get(id)
+    Repo.get(__MODULE__, id)
   end
 
+  @spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil
   def get(user_id, recipient) do
-    __MODULE__
-    |> Repo.get_by(user_id: user_id, recipient: recipient)
+    Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient)
   end
 
+  @spec get_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
+          {:ok, t()} | {:error, Ecto.Changeset.t()}
   def get_or_create(user_id, recipient) do
     %__MODULE__{}
     |> changeset(%{user_id: user_id, recipient: recipient})
@@ -60,6 +74,8 @@ defmodule Pleroma.Chat do
     )
   end
 
+  @spec bump_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
+          {:ok, t()} | {:error, Ecto.Changeset.t()}
   def bump_or_create(user_id, recipient) do
     %__MODULE__{}
     |> changeset(%{user_id: user_id, recipient: recipient})
index b1a0d26ab6e51f3315db9e7508c19717c6b91eef..8cbea9ec49f1459d04ef351511056c1c17f2e7c0 100644 (file)
@@ -158,7 +158,8 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
             "The messages in the chat",
             "application/json",
             chat_messages_response()
-          )
+          ),
+        404 => Operation.response("Not Found", "application/json", ApiError)
       },
       security: [
         %{
index 6445966e020c09b6e363599afb66baf2e2f51d53..69188a882049de1af680d78bc1aebecff6319e67 100644 (file)
@@ -48,13 +48,13 @@ defmodule Pleroma.Web.ControllerHelper do
 
   defp param_to_integer(_, default), do: default
 
-  def add_link_headers(conn, activities, extra_params \\ %{})
+  def add_link_headers(conn, entries, extra_params \\ %{})
 
-  def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _activities, _extra_params),
+  def add_link_headers(%{assigns: %{skip_link_headers: true}} = conn, _entries, _extra_params),
     do: conn
 
-  def add_link_headers(conn, activities, extra_params) do
-    case get_pagination_fields(conn, activities, extra_params) do
+  def add_link_headers(conn, entries, extra_params) do
+    case get_pagination_fields(conn, entries, extra_params) do
       %{"next" => next_url, "prev" => prev_url} ->
         put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
 
@@ -78,19 +78,15 @@ defmodule Pleroma.Web.ControllerHelper do
     }
   end
 
-  def get_pagination_fields(conn, activities, extra_params \\ %{}) do
-    case List.last(activities) do
+  def get_pagination_fields(conn, entries, extra_params \\ %{}) do
+    case List.last(entries) do
       %{pagination_id: max_id} when not is_nil(max_id) ->
-        %{pagination_id: min_id} =
-          activities
-          |> List.first()
+        %{pagination_id: min_id} = List.first(entries)
 
         build_pagination_fields(conn, min_id, max_id, extra_params)
 
       %{id: max_id} ->
-        %{id: min_id} =
-          activities
-          |> List.first()
+        %{id: min_id} = List.first(entries)
 
         build_pagination_fields(conn, min_id, max_id, extra_params)
 
index e8a1746d46a82d459dedc10799c58e92f4af2063..7b5f3daf9f975fe7d6d031727f9a2efdf0b62f80 100644 (file)
@@ -4,6 +4,8 @@
 defmodule Pleroma.Web.PleromaAPI.ChatController do
   use Pleroma.Web, :controller
 
+  import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
+
   alias Pleroma.Activity
   alias Pleroma.Chat
   alias Pleroma.Chat.MessageReference
@@ -47,7 +49,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
       }) do
     with %MessageReference{} = cm_ref <-
            MessageReference.get_by_id(message_id),
-         ^chat_id <- cm_ref.chat_id |> to_string(),
+         ^chat_id <- to_string(cm_ref.chat_id),
          %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
          {:ok, _} <- remove_or_delete(cm_ref, user) do
       conn
@@ -68,18 +70,13 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     end
   end
 
-  defp remove_or_delete(cm_ref, _) do
-    cm_ref
-    |> MessageReference.delete()
-  end
+  defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
 
   def post_chat_message(
-        %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
-        %{
-          id: id
-        }
+        %{body_params: params, assigns: %{user: user}} = conn,
+        %{id: id}
       ) do
-    with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
+    with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
          %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
          {:ok, activity} <-
            CommonAPI.post_chat_message(user, recipient, params[:content],
@@ -93,13 +90,12 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     end
   end
 
-  def mark_message_as_read(%{assigns: %{user: %{id: user_id}}} = conn, %{
-        id: chat_id,
-        message_id: message_id
-      }) do
-    with %MessageReference{} = cm_ref <-
-           MessageReference.get_by_id(message_id),
-         ^chat_id <- cm_ref.chat_id |> to_string(),
+  def mark_message_as_read(
+        %{assigns: %{user: %{id: user_id}}} = conn,
+        %{id: chat_id, message_id: message_id}
+      ) do
+    with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
+         ^chat_id <- to_string(cm_ref.chat_id),
          %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
          {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
       conn
@@ -109,36 +105,28 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
   end
 
   def mark_as_read(
-        %{
-          body_params: %{last_read_id: last_read_id},
-          assigns: %{user: %{id: user_id}}
-        } = conn,
+        %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
         %{id: id}
       ) do
-    with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
-         {_n, _} <-
-           MessageReference.set_all_seen_for_chat(chat, last_read_id) do
+    with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
+         {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
       conn
       |> put_view(ChatView)
       |> render("show.json", chat: chat)
     end
   end
 
-  def messages(%{assigns: %{user: %{id: user_id}}} = conn, %{id: id} = params) do
-    with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id) do
-      cm_refs =
+  def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
+    with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
+      chat_message_refs =
         chat
         |> MessageReference.for_chat_query()
         |> Pagination.fetch_paginated(params)
 
       conn
+      |> add_link_headers(chat_message_refs)
       |> put_view(MessageReferenceView)
-      |> render("index.json", chat_message_references: cm_refs)
-    else
-      _ ->
-        conn
-        |> put_status(:not_found)
-        |> json(%{error: "not found"})
+      |> render("index.json", chat_message_references: chat_message_refs)
     end
   end
 
@@ -158,8 +146,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     |> render("index.json", chats: chats)
   end
 
-  def create(%{assigns: %{user: user}} = conn, params) do
-    with %User{ap_id: recipient} <- User.get_by_id(params[:id]),
+  def create(%{assigns: %{user: user}} = conn, %{id: id}) do
+    with %User{ap_id: recipient} <- User.get_cached_by_id(id),
          {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
       conn
       |> put_view(ChatView)
@@ -167,8 +155,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     end
   end
 
-  def show(%{assigns: %{user: user}} = conn, params) do
-    with %Chat{} = chat <- Repo.get_by(Chat, user_id: user.id, id: params[:id]) do
+  def show(%{assigns: %{user: user}} = conn, %{id: id}) do
+    with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
       conn
       |> put_view(ChatView)
       |> render("show.json", chat: chat)
index 7be5fe09c16b119230659137c09e1e81ce2f106c..40f7c72cae771b9301c3e8d588b6901b13e1025a 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
-  use Pleroma.Web.ConnCase, async: true
+  use Pleroma.Web.ConnCase
 
   alias Pleroma.Chat
   alias Pleroma.Chat.MessageReference
@@ -184,17 +184,39 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
 
       chat = Chat.get(user.id, recipient.ap_id)
 
-      result =
-        conn
-        |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
-        |> json_response_and_validate_schema(200)
+      response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
+      result = json_response_and_validate_schema(response, 200)
+
+      [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
+      api_endpoint = "/api/v1/pleroma/chats/"
+
+      assert String.match?(
+               next,
+               ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
+             )
+
+      assert String.match?(
+               prev,
+               ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$)
+             )
 
       assert length(result) == 20
 
-      result =
-        conn
-        |> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
-        |> json_response_and_validate_schema(200)
+      response =
+        get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
+
+      result = json_response_and_validate_schema(response, 200)
+      [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
+
+      assert String.match?(
+               next,
+               ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
+             )
+
+      assert String.match?(
+               prev,
+               ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$)
+             )
 
       assert length(result) == 10
     end
@@ -223,12 +245,10 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
       assert length(result) == 3
 
       # Trying to get the chat of a different user
-      result =
-        conn
-        |> assign(:user, other_user)
-        |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
-
-      assert result |> json_response(404)
+      conn
+      |> assign(:user, other_user)
+      |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
+      |> json_response_and_validate_schema(404)
     end
   end