Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into issue/2115
authorlain <lain@soykaf.club>
Tue, 22 Sep 2020 15:13:46 +0000 (17:13 +0200)
committerlain <lain@soykaf.club>
Tue, 22 Sep 2020 15:13:46 +0000 (17:13 +0200)
1  2 
lib/pleroma/chat.ex
lib/pleroma/web/api_spec/operations/chat_operation.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
test/web/pleroma_api/controllers/chat_controller_test.exs

diff --combined lib/pleroma/chat.ex
index 202fffb8adf0d968271fe4401e035b464c06b5bc,84f8806a084c1ad957bc477af6d6b3ac8c88fd7f..28007cd9f4b945795d0774182342395ee47c85e6
@@@ -8,6 -8,7 +8,7 @@@ defmodule Pleroma.Chat d
    import Ecto.Changeset
    import Ecto.Query
  
+   alias Pleroma.Chat
    alias Pleroma.Repo
    alias Pleroma.User
  
@@@ -17,7 -18,6 +18,7 @@@
    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
      |> 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})
@@@ -74,8 -62,6 +75,8 @@@
      )
    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})
        conflict_target: [:user_id, :recipient]
      )
    end
+   @spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t()
+   def for_user_query(user_id) do
+     from(c in Chat,
+       where: c.user_id == ^user_id,
+       order_by: [desc: c.updated_at]
+     )
+   end
  end
index 8cbea9ec49f1459d04ef351511056c1c17f2e7c0,56554d5b4a3d0913733ad8391390ef4f94b56689..0dcfdb35467ef2e04f5493ef04546bb124bffd9b
@@@ -158,8 -158,7 +158,8 @@@ defmodule Pleroma.Web.ApiSpec.ChatOpera
              "The messages in the chat",
              "application/json",
              chat_messages_response()
 -          )
 +          ),
 +        404 => Operation.response("Not Found", "application/json", ApiError)
        },
        security: [
          %{
              "application/json",
              ChatMessage
            ),
-         400 => Operation.response("Bad Request", "application/json", ApiError)
+         400 => Operation.response("Bad Request", "application/json", ApiError),
+         422 => Operation.response("MRF Rejection", "application/json", ApiError)
        },
        security: [
          %{
index 7b5f3daf9f975fe7d6d031727f9a2efdf0b62f80,867cff8294af5b2835d1f687bf10e5de5c17cb9e..e667831c59c1b9d604208b4478c1ad15cbbd0a13
@@@ -4,8 -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
@@@ -49,7 -47,7 +49,7 @@@
        }) 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
      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],
        conn
        |> put_view(MessageReferenceView)
        |> render("show.json", chat_message_reference: cm_ref)
+     else
+       {:reject, message} ->
+         conn
+         |> put_status(:unprocessable_entity)
+         |> json(%{error: message})
+       {:error, message} ->
+         conn
+         |> put_status(:bad_request)
+         |> json(%{error: message})
      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
    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
  
      blocked_ap_ids = User.blocked_users_ap_ids(user)
  
      chats =
-       from(c in Chat,
-         where: c.user_id == ^user_id,
-         where: c.recipient not in ^blocked_ap_ids,
-         order_by: [desc: c.updated_at]
-       )
+       Chat.for_user_query(user_id)
+       |> where([c], c.recipient not in ^blocked_ap_ids)
        |> Repo.all()
  
      conn
      |> 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)
      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 40f7c72cae771b9301c3e8d588b6901b13e1025a,44a78a738f35527b3153db96dcce6c05a6fe9be5..11d5ba3739def5970ba9875dcd7658ac9b1ba3bf
@@@ -2,7 -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
          |> post("/api/v1/pleroma/chats/#{chat.id}/messages")
          |> json_response_and_validate_schema(400)
  
-       assert result
+       assert %{"error" => "no_content"} == result
      end
  
      test "it works with an attachment", %{conn: conn, user: user} do
  
        assert result["attachment"]
      end
+     test "gets MRF reason when rejected", %{conn: conn, user: user} do
+       clear_config([:mrf_keyword, :reject], ["GNO"])
+       clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+       other_user = insert(:user)
+       {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+       result =
+         conn
+         |> put_req_header("content-type", "application/json")
+         |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
+         |> json_response_and_validate_schema(422)
+       assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
+     end
    end
  
    describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" 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
        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