diff --git a/lib/service/activity/activity.ex b/lib/service/activity/activity.ex index e62facd32..3435f44e6 100644 --- a/lib/service/activity/activity.ex +++ b/lib/service/activity/activity.ex @@ -6,7 +6,8 @@ defmodule Mobilizon.Service.Activity do alias Mobilizon.Activities.Activity alias Mobilizon.Service.Activity.{Comment, Discussion, Event, Group, Member, Post, Resource} - @callback insert_activity(entity :: struct(), options :: map()) :: {:ok, Oban.Job.t()} + @callback insert_activity(entity :: struct(), options :: map()) :: + {:ok, Oban.Job.t()} | {:ok, any()} @callback get_object(object_id :: String.t() | integer()) :: struct() diff --git a/lib/service/activity/comment.ex b/lib/service/activity/comment.ex index af0cd7a13..dc08b2fb3 100644 --- a/lib/service/activity/comment.ex +++ b/lib/service/activity/comment.ex @@ -2,8 +2,8 @@ defmodule Mobilizon.Service.Activity.Comment do @moduledoc """ Insert a comment activity """ - alias Mobilizon.{Discussions, Events} alias Mobilizon.Actors.Actor + alias Mobilizon.{Discussions, Events} alias Mobilizon.Discussions.Comment alias Mobilizon.Events.Event alias Mobilizon.Service.Activity @@ -24,14 +24,16 @@ defmodule Mobilizon.Service.Activity.Comment do when not is_nil(actor_id) and not is_nil(event_id) do with {:ok, %Event{} = event} <- Events.get_event_with_preload(event_id) do - # Notify the actors mentionned - notify_mentionned(comment, event) + res = + [] + # Notify the actors mentionned + |> handle_notification(:mentionned, comment, event, options) + # Notify participants if there's a new announcement + |> handle_notification(:announcement, comment, event, options) + # Notify event organizer or group that there's new comments + |> handle_notification(:organizer, comment, event, options) - # Notify participants if there's a new announcement - notify_announcement(comment, event) - - # Notify event organizer or group that there's new comments - notify_organizer(comment, event, options) + {:ok, res} end end @@ -42,38 +44,76 @@ defmodule Mobilizon.Service.Activity.Comment do Discussions.get_comment(comment_id) end - defp notify_mentionned(%Comment{actor_id: actor_id, id: comment_id, mentions: mentions}, %Event{ - uuid: uuid, - title: title - }) + @common_params %{ + "type" => :comment, + "object_type" => :comment + } + + defp handle_notification(global_res, function, comment, event, options) do + case notify(function, comment, event, options) do + {:ok, res} -> Keyword.put(global_res, function, res) + _ -> Keyword.put(global_res, function, :error) + end + end + + @spec legacy_notifier_enqueue(map()) :: :ok + defp legacy_notifier_enqueue(args) do + LegacyNotifierBuilder.enqueue( + :legacy_notify, + @common_params |> Map.merge(maybe_inserted_at()) |> Map.merge(args) + ) + end + + @spec maybe_inserted_at :: map() + defp maybe_inserted_at do + if Application.fetch_env!(:mobilizon, :env) == :test do + %{} + else + %{"inserted_at" => DateTime.utc_now()} + end + end + + @type notification_type :: atom() + + # An actor is mentionned + @spec notify(notification_type(), Comment.t(), Event.t(), Keyword.t()) :: + {:ok, Oban.Job.t()} | {:ok, :skipped} + defp notify( + :mentionned, + %Comment{actor_id: actor_id, id: comment_id, mentions: mentions}, + %Event{ + uuid: uuid, + title: title + }, + _options + ) when length(mentions) > 0 do - LegacyNotifierBuilder.enqueue(:legacy_notify, %{ - "type" => :comment, + legacy_notifier_enqueue(%{ "subject" => :event_comment_mention, "subject_params" => %{ event_uuid: uuid, event_title: title }, "author_id" => actor_id, - "object_type" => :comment, "object_id" => to_string(comment_id), - "inserted_at" => DateTime.utc_now(), "mentions" => Enum.map(mentions, & &1.actor_id) }) + + {:ok, :enqueued} end - defp notify_mentionned(_, _), do: {:ok, :skipped} - - defp notify_announcement( + # An event has a new announcement, send it to the participants + defp notify( + :announcement, %Comment{actor_id: actor_id, is_announcement: true, id: comment_id}, %Event{ id: event_id, uuid: uuid, title: title - } + }, + _options ) do - LegacyNotifierBuilder.enqueue(:legacy_notify, %{ - "type" => :comment, + legacy_notifier_enqueue(%{ "subject" => :participation_event_comment, "subject_params" => %{ event_id: event_id, @@ -81,20 +121,17 @@ defmodule Mobilizon.Service.Activity.Comment do event_title: title }, "author_id" => actor_id, - "object_type" => :comment, - "object_id" => to_string(comment_id), - "inserted_at" => DateTime.utc_now() + "object_id" => to_string(comment_id) }) + + {:ok, :enqueued} end - defp notify_announcement(_, _), do: {:ok, :skipped} - - @spec notify_organizer(Comment.t(), Event.t(), Keyword.t()) :: - {:ok, Oban.Job.t()} | {:ok, :skipped} - defp notify_organizer( + # A group event has a new comment, send it as an activity + defp notify( + :announcement, %Comment{ actor_id: actor_id, - is_announcement: true, in_reply_to_comment_id: in_reply_to_comment_id, id: comment_id }, @@ -119,12 +156,15 @@ defmodule Mobilizon.Service.Activity.Comment do "object_id" => to_string(comment_id), "inserted_at" => DateTime.utc_now() }) + + {:ok, :enqueued} end - defp notify_organizer( + # An event has a new comment, send it to the organizer + defp notify( + :organizer, %Comment{ actor_id: actor_id, - is_announcement: true, in_reply_to_comment_id: in_reply_to_comment_id, id: comment_id }, @@ -137,8 +177,7 @@ defmodule Mobilizon.Service.Activity.Comment do _options ) when actor_id !== organizer_actor_id do - LegacyNotifierBuilder.enqueue(:legacy_notify, %{ - "type" => :comment, + legacy_notifier_enqueue(%{ "subject" => :event_new_comment, "subject_params" => %{ event_title: title, @@ -146,11 +185,11 @@ defmodule Mobilizon.Service.Activity.Comment do comment_reply_to: !is_nil(in_reply_to_comment_id) }, "author_id" => actor_id, - "object_type" => :comment, - "object_id" => to_string(comment_id), - "inserted_at" => DateTime.utc_now() + "object_id" => to_string(comment_id) }) + + {:ok, :enqueued} end - defp notify_organizer(_, _, _), do: {:ok, :skipped} + defp notify(_, _, _, _), do: {:ok, :skipped} end diff --git a/test/service/activity/comment_test.exs b/test/service/activity/comment_test.exs new file mode 100644 index 000000000..93b7de8cb --- /dev/null +++ b/test/service/activity/comment_test.exs @@ -0,0 +1,131 @@ +defmodule Mobilizon.Service.Activity.CommentTest do + @moduledoc """ + Test the Comment activity provider module + """ + + alias Mobilizon.Actors.Actor + alias Mobilizon.Discussions.Comment + alias Mobilizon.Events.Event + alias Mobilizon.Mention + alias Mobilizon.Service.Activity.Comment, as: CommentActivity + alias Mobilizon.Service.Workers.LegacyNotifierBuilder + alias Mobilizon.Users.User + + use Mobilizon.DataCase, async: true + use Oban.Testing, repo: Mobilizon.Storage.Repo + import Mobilizon.Factory + + describe "handle comment with mentions" do + test "with no mentions" do + %Event{title: event_title, uuid: event_uuid} = event = insert(:event) + %Comment{id: comment_id, actor_id: author_id} = comment = insert(:comment, event: event) + + assert [organizer: :enqueued, announcement: :skipped, mentionned: :skipped] == + CommentActivity.insert_activity(comment) + + refute_enqueued( + worker: LegacyNotifierBuilder, + args: %{op: :event_comment_mention} + ) + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => author_id, + "object_id" => to_string(comment_id), + "object_type" => "comment", + "op" => "legacy_notify", + "subject" => "event_new_comment", + "subject_params" => %{ + "event_title" => event_title, + "event_uuid" => event_uuid, + "comment_reply_to" => false + }, + "type" => "comment" + } + ) + end + + test "with some mentions" do + %User{} = user = insert(:user) + %Actor{id: actor_id} = actor = insert(:actor, user: user) + %Event{uuid: event_uuid, title: event_title} = event = insert(:event) + + %Comment{id: comment_id, actor_id: author_id} = + comment = insert(:comment, text: "Hey @you", event: event) + + comment = %Comment{ + comment + | mentions: [ + %Mention{actor: actor, event: event, comment: comment, actor_id: actor_id} + ] + } + + assert [organizer: :enqueued, announcement: :skipped, mentionned: :enqueued] == + CommentActivity.insert_activity(comment) + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => author_id, + "mentions" => [actor_id], + "object_id" => to_string(comment_id), + "object_type" => "comment", + "op" => "legacy_notify", + "subject" => "event_comment_mention", + "subject_params" => %{ + "event_title" => event_title, + "event_uuid" => event_uuid + }, + "type" => "comment" + } + ) + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => author_id, + "object_id" => to_string(comment_id), + "object_type" => "comment", + "op" => "legacy_notify", + "subject" => "event_new_comment", + "subject_params" => %{ + "event_title" => event_title, + "event_uuid" => event_uuid, + "comment_reply_to" => false + }, + "type" => "comment" + } + ) + end + end + + describe "handle comment which is an announcement" do + test "schedules a notification for the participants" do + %Event{uuid: event_uuid, title: event_title, id: event_id} = event = insert(:event) + + %Comment{id: comment_id, actor_id: author_id} = + comment = insert(:comment, text: "Hey you", event: event, is_announcement: true) + + assert [organizer: :enqueued, announcement: :enqueued, mentionned: :skipped] == + CommentActivity.insert_activity(comment) + + assert_enqueued( + worker: LegacyNotifierBuilder, + args: %{ + "author_id" => author_id, + "object_id" => to_string(comment_id), + "object_type" => "comment", + "op" => "legacy_notify", + "subject" => "participation_event_comment", + "subject_params" => %{ + "event_title" => event_title, + "event_uuid" => event_uuid, + "event_id" => event_id + }, + "type" => "comment" + } + ) + end + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index e956f255d..cd8bd7848 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -156,6 +156,7 @@ defmodule Mobilizon.Factory do deleted_at: nil, tags: build_list(3, :tag), in_reply_to_comment: nil, + is_announcement: false, published_at: DateTime.utc_now(), url: Routes.page_url(Endpoint, :comment, uuid) }