@@ -0,0 +1,74 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.ScheduledActivity do | |||
use Ecto.Schema | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
import Ecto.Query | |||
import Ecto.Changeset | |||
schema "scheduled_activities" do | |||
belongs_to(:user, User, type: Pleroma.FlakeId) | |||
field(:scheduled_at, :naive_datetime) | |||
field(:params, :map) | |||
timestamps() | |||
end | |||
def changeset(%ScheduledActivity{} = scheduled_activity, attrs) do | |||
scheduled_activity | |||
|> cast(attrs, [:scheduled_at, :params]) | |||
end | |||
def update_changeset(%ScheduledActivity{} = scheduled_activity, attrs) do | |||
scheduled_activity | |||
|> cast(attrs, [:scheduled_at]) | |||
end | |||
def new(%User{} = user, attrs) do | |||
%ScheduledActivity{user_id: user.id} | |||
|> changeset(attrs) | |||
end | |||
def create(%User{} = user, attrs) do | |||
user | |||
|> new(attrs) | |||
|> Repo.insert() | |||
end | |||
def get(%User{} = user, scheduled_activity_id) do | |||
ScheduledActivity | |||
|> where(user_id: ^user.id) | |||
|> where(id: ^scheduled_activity_id) | |||
|> Repo.one() | |||
end | |||
def update(%User{} = user, scheduled_activity_id, attrs) do | |||
with %ScheduledActivity{} = scheduled_activity <- get(user, scheduled_activity_id) do | |||
scheduled_activity | |||
|> update_changeset(attrs) | |||
|> Repo.update() | |||
else | |||
nil -> {:error, :not_found} | |||
end | |||
end | |||
def delete(%User{} = user, scheduled_activity_id) do | |||
with %ScheduledActivity{} = scheduled_activity <- get(user, scheduled_activity_id) do | |||
scheduled_activity | |||
|> Repo.delete() | |||
else | |||
nil -> {:error, :not_found} | |||
end | |||
end | |||
def for_user_query(%User{} = user) do | |||
ScheduledActivity | |||
|> where(user_id: ^user.id) | |||
end | |||
end |
@@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
alias Pleroma.Activity | |||
alias Pleroma.Notification | |||
alias Pleroma.Pagination | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
def get_followers(user, params \\ %{}) do | |||
@@ -28,6 +29,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do | |||
|> Pagination.fetch_paginated(params) | |||
end | |||
def get_scheduled_activities(user, params \\ %{}) do | |||
user | |||
|> ScheduledActivity.for_user_query() | |||
|> Pagination.fetch_paginated(params) | |||
end | |||
defp cast_params(params) do | |||
param_types = %{ | |||
exclude_types: {:array, :string} | |||
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Stats | |||
alias Pleroma.User | |||
alias Pleroma.Web | |||
@@ -25,6 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
alias Pleroma.Web.MastodonAPI.MastodonView | |||
alias Pleroma.Web.MastodonAPI.NotificationView | |||
alias Pleroma.Web.MastodonAPI.ReportView | |||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.MediaProxy | |||
alias Pleroma.Web.OAuth.App | |||
@@ -364,6 +366,45 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
end | |||
end | |||
def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do | |||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do | |||
conn | |||
|> add_link_headers(:scheduled_statuses, scheduled_activities) | |||
|> put_view(ScheduledActivityView) | |||
|> render("index.json", %{scheduled_activities: scheduled_activities}) | |||
end | |||
end | |||
def show_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do | |||
with %ScheduledActivity{} = scheduled_activity <- | |||
ScheduledActivity.get(user, scheduled_activity_id) do | |||
conn | |||
|> put_view(ScheduledActivityView) | |||
|> render("show.json", %{scheduled_activity: scheduled_activity}) | |||
else | |||
_ -> {:error, :not_found} | |||
end | |||
end | |||
def update_scheduled_status( | |||
%{assigns: %{user: user}} = conn, | |||
%{"id" => scheduled_activity_id} = params | |||
) do | |||
with {:ok, scheduled_activity} <- | |||
ScheduledActivity.update(user, scheduled_activity_id, params) do | |||
conn | |||
|> put_view(ScheduledActivityView) | |||
|> render("show.json", %{scheduled_activity: scheduled_activity}) | |||
end | |||
end | |||
def delete_scheduled_status(%{assigns: %{user: user}} = conn, %{"id" => scheduled_activity_id}) do | |||
with {:ok, %ScheduledActivity{}} <- ScheduledActivity.delete(user, scheduled_activity_id) do | |||
conn | |||
|> json(%{}) | |||
end | |||
end | |||
def post_status(conn, %{"status" => "", "media_ids" => media_ids} = params) | |||
when length(media_ids) > 0 do | |||
params = | |||
@@ -1406,6 +1447,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
# fallback action | |||
# | |||
def errors(conn, {:error, :not_found}) do | |||
conn | |||
|> put_status(404) | |||
|> json(%{error: "Record not found"}) | |||
end | |||
def errors(conn, _) do | |||
conn | |||
|> put_status(500) | |||
@@ -0,0 +1,23 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do | |||
use Pleroma.Web, :view | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView | |||
def render("index.json", %{scheduled_activities: scheduled_activities}) do | |||
render_many(scheduled_activities, ScheduledActivityView, "show.json") | |||
end | |||
def render("show.json", %{scheduled_activity: %ScheduledActivity{} = scheduled_activity}) do | |||
%{ | |||
id: scheduled_activity.id |> to_string, | |||
scheduled_at: scheduled_activity.scheduled_at |> CommonAPI.Utils.to_masto_date(), | |||
params: scheduled_activity.params | |||
} | |||
end | |||
end |
@@ -244,6 +244,9 @@ defmodule Pleroma.Web.Router do | |||
get("/notifications", MastodonAPIController, :notifications) | |||
get("/notifications/:id", MastodonAPIController, :get_notification) | |||
get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses) | |||
get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status) | |||
get("/lists", MastodonAPIController, :get_lists) | |||
get("/lists/:id", MastodonAPIController, :get_list) | |||
get("/lists/:id/accounts", MastodonAPIController, :list_accounts) | |||
@@ -278,6 +281,9 @@ defmodule Pleroma.Web.Router do | |||
post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) | |||
post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) | |||
put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status) | |||
delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status) | |||
post("/media", MastodonAPIController, :upload) | |||
put("/media/:id", MastodonAPIController, :update_media) | |||
@@ -0,0 +1,15 @@ | |||
defmodule Pleroma.Repo.Migrations.CreateScheduledActivities do | |||
use Ecto.Migration | |||
def change do | |||
create table(:scheduled_activities) do | |||
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all)) | |||
add(:scheduled_at, :naive_datetime, null: false) | |||
add(:params, :map, null: false) | |||
timestamps() | |||
end | |||
create(index(:scheduled_activities, [:scheduled_at])) | |||
end | |||
end |
@@ -23,6 +23,14 @@ defmodule Pleroma.Factory do | |||
} | |||
end | |||
def scheduled_activity_factory do | |||
%Pleroma.ScheduledActivity{ | |||
user: build(:user), | |||
scheduled_at: NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(60), :millisecond), | |||
params: build(:note) |> Map.from_struct() |> Map.get(:data) | |||
} | |||
end | |||
def note_factory(attrs \\ %{}) do | |||
text = sequence(:text, &"This is :moominmamma: note #{&1}") | |||
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.ScheduledActivity | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ActivityPub | |||
alias Pleroma.Web.CommonAPI | |||
@@ -2407,4 +2408,107 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do | |||
assert redirected_to(conn) == "/web/getting-started" | |||
end | |||
end | |||
describe "scheduled activities" do | |||
test "shows scheduled activities", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() | |||
scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() | |||
conn = | |||
conn | |||
|> assign(:user, user) | |||
# min_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
# since_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result | |||
# max_id | |||
conn_res = | |||
conn | |||
|> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") | |||
result = json_response(conn_res, 200) | |||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result | |||
end | |||
test "shows a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) | |||
assert scheduled_activity_id == scheduled_activity.id |> to_string() | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> get("/api/v1/scheduled_statuses/404") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "updates a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
new_scheduled_at = | |||
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ | |||
scheduled_at: new_scheduled_at | |||
}) | |||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) | |||
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
test "deletes a scheduled activity", %{conn: conn} do | |||
user = insert(:user) | |||
scheduled_activity = insert(:scheduled_activity, user: user) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{} = json_response(res_conn, 200) | |||
assert nil == Repo.get(ScheduledActivity, scheduled_activity.id) | |||
res_conn = | |||
conn | |||
|> assign(:user, user) | |||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") | |||
assert %{"error" => "Record not found"} = json_response(res_conn, 404) | |||
end | |||
end | |||
end |