Chat: Allow posting without content if an attachment is present.
authorlain <lain@soykaf.club>
Wed, 13 May 2020 13:31:28 +0000 (15:31 +0200)
committerlain <lain@soykaf.club>
Wed, 13 May 2020 13:31:28 +0000 (15:31 +0200)
docs/API/chats.md
lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
lib/pleroma/web/api_spec/operations/chat_operation.ex
lib/pleroma/web/api_spec/schemas/chat_message.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
test/web/activity_pub/object_validator_test.exs
test/web/common_api/common_api_test.exs
test/web/pleroma_api/controllers/chat_controller_test.exs

index ad36961aef3be69eb5aba6430816636e2fd26767..1ea18ff5fd773b6827e88caea7162acf4522ec5d 100644 (file)
@@ -166,11 +166,10 @@ Posting a chat message for given Chat id works like this:
 `POST /api/v1/pleroma/chats/{id}/messages`
 
 Parameters:
-- content: The text content of the message
+- content: The text content of the message. Optional if media is attached.
 - media_id: The id of an upload that will be attached to the message.
 
-Currently, no formatting beyond basic escaping and emoji is implemented, as well as no
-attachments. This will most probably change.
+Currently, no formatting beyond basic escaping and emoji is implemented.
 
 Returned data:
 
index e40c80ab45a80df8e0537ce2a03c5ab64e53ef43..9c20c188ae78e784b4af8fa834e076dd041e9b28 100644 (file)
@@ -61,12 +61,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
   def validate_data(data_cng) do
     data_cng
     |> validate_inclusion(:type, ["ChatMessage"])
-    |> validate_required([:id, :actor, :to, :type, :content, :published])
+    |> validate_required([:id, :actor, :to, :type, :published])
+    |> validate_content_or_attachment()
     |> validate_length(:to, is: 1)
     |> validate_length(:content, max: Pleroma.Config.get([:instance, :remote_limit]))
     |> validate_local_concern()
   end
 
+  def validate_content_or_attachment(cng) do
+    attachment = get_field(cng, :attachment)
+
+    if attachment do
+      cng
+    else
+      cng
+      |> validate_required([:content])
+    end
+  end
+
   @doc """
   Validates the following
   - If both users are in our system
index 8ba10c60338eddb338960f2da2c9e64fbe426d57..a1c5db5dcd283569592d62a23da78069d9e30470 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.ApiSpec.ChatOperation do
   alias OpenApiSpex.Operation
   alias OpenApiSpex.Schema
+  alias Pleroma.Web.ApiSpec.Schemas.ApiError
   alias Pleroma.Web.ApiSpec.Schemas.Chat
   alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
 
@@ -149,14 +150,15 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
       parameters: [
         Operation.parameter(:id, :path, :string, "The ID of the Chat")
       ],
-      requestBody: request_body("Parameters", chat_message_create(), required: true),
+      requestBody: request_body("Parameters", chat_message_create()),
       responses: %{
         200 =>
           Operation.response(
             "The newly created ChatMessage",
             "application/json",
             ChatMessage
-          )
+          ),
+        400 => Operation.response("Bad Request", "application/json", ApiError)
       },
       security: [
         %{
@@ -292,10 +294,12 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
       description: "POST body for creating an chat message",
       type: :object,
       properties: %{
-        content: %Schema{type: :string, description: "The content of your message"},
+        content: %Schema{
+          type: :string,
+          description: "The content of your message. Optional if media_id is present"
+        },
         media_id: %Schema{type: :string, description: "The id of an upload"}
       },
-      required: [:content],
       example: %{
         "content" => "Hey wanna buy feet pics?",
         "media_id" => "134234"
index 6e8f1a10ad87a499db5d24bc0a9654e6622bdf77..3ee85aa761928c632d774cf88603dfa56ce650b9 100644 (file)
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do
       id: %Schema{type: :string},
       account_id: %Schema{type: :string, description: "The Mastodon API id of the actor"},
       chat_id: %Schema{type: :string},
-      content: %Schema{type: :string},
+      content: %Schema{type: :string, nullable: true},
       created_at: %Schema{type: :string, format: :"date-time"},
       emojis: %Schema{type: :array},
       attachment: %Schema{type: :object, nullable: true}
index 664175a4fe063cbba9692e24c493c6811b0f4d4b..7008cea4425f1ef6d27470de94a6f4db9e57c420 100644 (file)
@@ -26,14 +26,14 @@ defmodule Pleroma.Web.CommonAPI do
   require Logger
 
   def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
-    with :ok <- validate_chat_content_length(content),
-         maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
+    with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
+         :ok <- validate_chat_content_length(content, !!maybe_attachment),
          {_, {:ok, chat_message_data, _meta}} <-
            {:build_object,
             Builder.chat_message(
               user,
               recipient.ap_id,
-              content |> Formatter.html_escape("text/plain"),
+              content |> format_chat_content,
               attachment: maybe_attachment
             )},
          {_, {:ok, create_activity_data, _meta}} <-
@@ -47,7 +47,16 @@ defmodule Pleroma.Web.CommonAPI do
     end
   end
 
-  defp validate_chat_content_length(content) do
+  defp format_chat_content(nil), do: nil
+
+  defp format_chat_content(content) do
+    content |> Formatter.html_escape("text/plain")
+  end
+
+  defp validate_chat_content_length(_, true), do: :ok
+  defp validate_chat_content_length(nil, false), do: {:error, :no_content}
+
+  defp validate_chat_content_length(content, _) do
     if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do
       :ok
     else
index 496cb8e876105919cd5e3094db80e82709239481..210c8ec4a06d949493635b4b794d41f121467eb1 100644 (file)
@@ -58,8 +58,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
   end
 
   def post_chat_message(
-        %{body_params: %{content: content} = params, assigns: %{user: %{id: user_id} = user}} =
-          conn,
+        %{body_params: params, assigns: %{user: %{id: user_id} = user}} = conn,
         %{
           id: id
         }
@@ -67,7 +66,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
     with %Chat{} = chat <- Repo.get_by(Chat, id: id, user_id: user_id),
          %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
          {:ok, activity} <-
-           CommonAPI.post_chat_message(user, recipient, content, media_id: params[:media_id]),
+           CommonAPI.post_chat_message(user, recipient, params[:content],
+             media_id: params[:media_id]
+           ),
          message <- Object.normalize(activity) do
       conn
       |> put_view(ChatMessageView)
index d9f5a8facb6ca2a2454c88b07e0f6d4e870117f6..da33d3dbc0b8b732c7a30e264cbc650ee91201a9 100644 (file)
@@ -103,6 +103,38 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
       assert object["attachment"]
     end
 
+    test "validates for a basic object with an attachment but without content", %{
+      valid_chat_message: valid_chat_message,
+      user: user
+    } do
+      file = %Plug.Upload{
+        content_type: "image/jpg",
+        path: Path.absname("test/fixtures/image.jpg"),
+        filename: "an_image.jpg"
+      }
+
+      {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
+
+      valid_chat_message =
+        valid_chat_message
+        |> Map.put("attachment", attachment.data)
+        |> Map.delete("content")
+
+      assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
+
+      assert object["attachment"]
+    end
+
+    test "does not validate if the message has no content", %{
+      valid_chat_message: valid_chat_message
+    } do
+      contentless =
+        valid_chat_message
+        |> Map.delete("content")
+
+      refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, []))
+    end
+
     test "does not validate if the message is longer than the remote_limit", %{
       valid_chat_message: valid_chat_message
     } do
index fd2c486a148564b8b80a2e87b07b0e7add9849e0..46ffd2888a198c8b7232407e18399db531608686 100644 (file)
@@ -27,6 +27,29 @@ defmodule Pleroma.Web.CommonAPITest do
   describe "posting chat messages" do
     setup do: clear_config([:instance, :chat_limit])
 
+    test "it posts a chat message without content but with an attachment" do
+      author = insert(:user)
+      recipient = insert(:user)
+
+      file = %Plug.Upload{
+        content_type: "image/jpg",
+        path: Path.absname("test/fixtures/image.jpg"),
+        filename: "an_image.jpg"
+      }
+
+      {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
+
+      {:ok, activity} =
+        CommonAPI.post_chat_message(
+          author,
+          recipient,
+          nil,
+          media_id: upload.id
+        )
+
+      assert activity
+    end
+
     test "it posts a chat message" do
       author = insert(:user)
       recipient = insert(:user)
index 037dd229728b696f4d99cf0a35c54c0ed2ed4cd9..d79aa3148d4b391a578a63bcfff554730e055180 100644 (file)
@@ -53,6 +53,20 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
       assert result["chat_id"] == chat.id |> to_string()
     end
 
+    test "it fails if there is no content", %{conn: conn, user: user} do
+      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")
+        |> json_response_and_validate_schema(400)
+
+      assert result
+    end
+
     test "it works with an attachment", %{conn: conn, user: user} do
       file = %Plug.Upload{
         content_type: "image/jpg",
@@ -70,13 +84,11 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
         conn
         |> put_req_header("content-type", "application/json")
         |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
-          "content" => "Hallo!!",
           "media_id" => to_string(upload.id)
         })
         |> json_response_and_validate_schema(200)
 
-      assert result["content"] == "Hallo!!"
-      assert result["chat_id"] == chat.id |> to_string()
+      assert result["attachment"]
     end
   end