Handle `scheduled_at` on status creation.
authoreugenijm <eugenijm@protonmail.com>
Sat, 30 Mar 2019 09:58:40 +0000 (12:58 +0300)
committereugenijm <eugenijm@protonmail.com>
Sat, 6 Apr 2019 20:55:58 +0000 (23:55 +0300)
lib/pleroma/activity.ex
lib/pleroma/scheduled_activity.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
test/web/mastodon_api/mastodon_api_controller_test.exs

index bc3f8caba55884ad236708300afce2c7fcff51bc..ab8861b277b7683660e6ca031dd8f69db664522c 100644 (file)
@@ -31,7 +31,7 @@ defmodule Pleroma.Activity do
     field(:data, :map)
     field(:local, :boolean, default: true)
     field(:actor, :string)
-    field(:recipients, {:array, :string})
+    field(:recipients, {:array, :string}, default: [])
     has_many(:notifications, Notification, on_delete: :delete_all)
 
     # Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
index 0c1b26a33d4dd5ee0e2bb6a4c0f70a4fac0e3fe0..9fdc1399087ce43e7529014d5c6f147155029799 100644 (file)
@@ -12,6 +12,8 @@ defmodule Pleroma.ScheduledActivity do
   import Ecto.Query
   import Ecto.Changeset
 
+  @min_offset :timer.minutes(5)
+
   schema "scheduled_activities" do
     belongs_to(:user, User, type: Pleroma.FlakeId)
     field(:scheduled_at, :naive_datetime)
@@ -30,6 +32,20 @@ defmodule Pleroma.ScheduledActivity do
     |> cast(attrs, [:scheduled_at])
   end
 
+  def far_enough?(scheduled_at) when is_binary(scheduled_at) do
+    with {:ok, scheduled_at} <- Ecto.Type.cast(:naive_datetime, scheduled_at) do
+      far_enough?(scheduled_at)
+    else
+      _ -> false
+    end
+  end
+
+  def far_enough?(scheduled_at) do
+    now = NaiveDateTime.utc_now()
+    diff = NaiveDateTime.diff(scheduled_at, now, :millisecond)
+    diff > @min_offset
+  end
+
   def new(%User{} = user, attrs) do
     %ScheduledActivity{user_id: user.id}
     |> changeset(attrs)
index 0916d84dc77fc0e152ccf0148be5e8d1948c22f6..863fc395410f93e3e21bd0b905971fd4a63c4b81 100644 (file)
@@ -425,12 +425,29 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
         _ -> Ecto.UUID.generate()
       end
 
-    {:ok, activity} =
-      Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ -> CommonAPI.post(user, params) end)
+    scheduled_at = params["scheduled_at"]
 
-    conn
-    |> put_view(StatusView)
-    |> try_render("status.json", %{activity: activity, for: user, as: :activity})
+    if scheduled_at && ScheduledActivity.far_enough?(scheduled_at) do
+      {:ok, scheduled_activity} =
+        Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ ->
+          ScheduledActivity.create(user, %{"params" => params, "scheduled_at" => scheduled_at})
+        end)
+
+      conn
+      |> put_view(ScheduledActivityView)
+      |> render("show.json", %{scheduled_activity: scheduled_activity})
+    else
+      params = Map.drop(params, ["scheduled_at"])
+
+      {:ok, activity} =
+        Cachex.fetch!(:idempotency_cache, idempotency_key, fn _ ->
+          CommonAPI.post(user, params)
+        end)
+
+      conn
+      |> put_view(StatusView)
+      |> try_render("status.json", %{activity: activity, for: user, as: :activity})
+    end
   end
 
   def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
index 864c0ad4de1385f7ecd8503e56f225bbdc91cc58..0ec66ab73aeab859b53984a1a5c2b36d9473f34a 100644 (file)
@@ -2410,6 +2410,42 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
   end
 
   describe "scheduled activities" do
+    test "creates a scheduled activity", %{conn: conn} do
+      user = insert(:user)
+      scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> post("/api/v1/statuses", %{
+          "status" => "scheduled",
+          "scheduled_at" => scheduled_at
+        })
+
+      assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
+      assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
+      assert [] == Repo.all(Activity)
+    end
+
+    test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
+         %{conn: conn} do
+      user = insert(:user)
+
+      scheduled_at =
+        NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
+
+      conn =
+        conn
+        |> assign(:user, user)
+        |> post("/api/v1/statuses", %{
+          "status" => "not scheduled",
+          "scheduled_at" => scheduled_at
+        })
+
+      assert %{"content" => "not scheduled"} = json_response(conn, 200)
+      assert [] == Repo.all(ScheduledActivity)
+    end
+
     test "shows scheduled activities", %{conn: conn} do
       user = insert(:user)
       scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()