diff --git a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex
new file mode 100644
index 000000000..fe675a923
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex
@@ -0,0 +1,96 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.ScheduledActivityOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+ alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def index_operation do
+ %Operation{
+ tags: ["Scheduled Statuses"],
+ summary: "View scheduled statuses",
+ security: [%{"oAuth" => ["read:statuses"]}],
+ parameters: pagination_params(),
+ operationId: "ScheduledActivity.index",
+ responses: %{
+ 200 =>
+ Operation.response("Array of ScheduledStatus", "application/json", %Schema{
+ type: :array,
+ items: ScheduledStatus
+ })
+ }
+ }
+ end
+
+ def show_operation do
+ %Operation{
+ tags: ["Scheduled Statuses"],
+ summary: "View a single scheduled status",
+ security: [%{"oAuth" => ["read:statuses"]}],
+ parameters: [id_param()],
+ operationId: "ScheduledActivity.show",
+ responses: %{
+ 200 => Operation.response("Scheduled Status", "application/json", ScheduledStatus),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def update_operation do
+ %Operation{
+ tags: ["Scheduled Statuses"],
+ summary: "Schedule a status",
+ operationId: "ScheduledActivity.update",
+ security: [%{"oAuth" => ["write:statuses"]}],
+ parameters: [id_param()],
+ requestBody:
+ request_body("Parameters", %Schema{
+ type: :object,
+ properties: %{
+ scheduled_at: %Schema{
+ type: :string,
+ format: :"date-time",
+ description:
+ "ISO 8601 Datetime at which the status will be published. Must be at least 5 minutes into the future."
+ }
+ }
+ }),
+ responses: %{
+ 200 => Operation.response("Scheduled Status", "application/json", ScheduledStatus),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def delete_operation do
+ %Operation{
+ tags: ["Scheduled Statuses"],
+ summary: "Cancel a scheduled status",
+ security: [%{"oAuth" => ["write:statuses"]}],
+ parameters: [id_param()],
+ operationId: "ScheduledActivity.delete",
+ responses: %{
+ 200 => Operation.response("Empty object", "application/json", %Schema{type: :object}),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp id_param do
+ Operation.parameter(:id, :path, FlakeID, "Poll ID",
+ example: "123",
+ required: true
+ )
+ end
+end
diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex
new file mode 100644
index 000000000..c146c416e
--- /dev/null
+++ b/lib/pleroma/web/api_spec/schemas/attachment.ex
@@ -0,0 +1,68 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do
+ alias OpenApiSpex.Schema
+
+ require OpenApiSpex
+
+ OpenApiSpex.schema(%{
+ title: "Attachment",
+ description: "Represents a file or media attachment that can be added to a status.",
+ type: :object,
+ requried: [:id, :url, :preview_url],
+ properties: %{
+ id: %Schema{type: :string},
+ url: %Schema{
+ type: :string,
+ format: :uri,
+ description: "The location of the original full-size attachment"
+ },
+ remote_url: %Schema{
+ type: :string,
+ format: :uri,
+ description:
+ "The location of the full-size original attachment on the remote website. String (URL), or null if the attachment is local",
+ nullable: true
+ },
+ preview_url: %Schema{
+ type: :string,
+ format: :uri,
+ description: "The location of a scaled-down preview of the attachment"
+ },
+ text_url: %Schema{
+ type: :string,
+ format: :uri,
+ description: "A shorter URL for the attachment"
+ },
+ description: %Schema{
+ type: :string,
+ nullable: true,
+ description:
+ "Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load"
+ },
+ type: %Schema{
+ type: :string,
+ enum: ["image", "video", "audio", "unknown"],
+ description: "The type of the attachment"
+ },
+ pleroma: %Schema{
+ type: :object,
+ properties: %{
+ mime_type: %Schema{type: :string, description: "mime type of the attachment"}
+ }
+ }
+ },
+ example: %{
+ id: "1638338801",
+ type: "image",
+ url: "someurl",
+ remote_url: "someurl",
+ preview_url: "someurl",
+ text_url: "someurl",
+ description: nil,
+ pleroma: %{mime_type: "image/png"}
+ }
+ })
+end
diff --git a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex
new file mode 100644
index 000000000..0520d0848
--- /dev/null
+++ b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex
@@ -0,0 +1,54 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.Attachment
+ alias Pleroma.Web.ApiSpec.Schemas.Poll
+ alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
+
+ require OpenApiSpex
+
+ OpenApiSpex.schema(%{
+ title: "ScheduledStatus",
+ description: "Represents a status that will be published at a future scheduled date.",
+ type: :object,
+ required: [:id, :scheduled_at, :params],
+ properties: %{
+ id: %Schema{type: :string},
+ scheduled_at: %Schema{type: :string, format: :"date-time"},
+ media_attachments: %Schema{type: :array, items: Attachment},
+ params: %Schema{
+ type: :object,
+ required: [:text, :visibility],
+ properties: %{
+ text: %Schema{type: :string, nullable: true},
+ media_ids: %Schema{type: :array, nullable: true, items: %Schema{type: :string}},
+ sensitive: %Schema{type: :boolean, nullable: true},
+ spoiler_text: %Schema{type: :string, nullable: true},
+ visibility: %Schema{type: VisibilityScope, nullable: true},
+ scheduled_at: %Schema{type: :string, format: :"date-time", nullable: true},
+ poll: %Schema{type: Poll, nullable: true},
+ in_reply_to_id: %Schema{type: :string, nullable: true}
+ }
+ }
+ },
+ example: %{
+ id: "3221",
+ scheduled_at: "2019-12-05T12:33:01.000Z",
+ params: %{
+ text: "test content",
+ media_ids: nil,
+ sensitive: nil,
+ spoiler_text: nil,
+ visibility: nil,
+ scheduled_at: nil,
+ poll: nil,
+ idempotency: nil,
+ in_reply_to_id: nil
+ },
+ media_attachments: [Attachment.schema().example]
+ }
+ })
+end
diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex
index aef0588d4..d44636a48 100644
--- a/lib/pleroma/web/api_spec/schemas/status.ex
+++ b/lib/pleroma/web/api_spec/schemas/status.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ApiSpec.Schemas.Status do
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
+ alias Pleroma.Web.ApiSpec.Schemas.Attachment
alias Pleroma.Web.ApiSpec.Schemas.Emoji
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Poll
@@ -50,22 +51,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
language: %Schema{type: :string, nullable: true},
media_attachments: %Schema{
type: :array,
- items: %Schema{
- type: :object,
- properties: %{
- id: %Schema{type: :string},
- url: %Schema{type: :string, format: :uri},
- remote_url: %Schema{type: :string, format: :uri},
- preview_url: %Schema{type: :string, format: :uri},
- text_url: %Schema{type: :string, format: :uri},
- description: %Schema{type: :string},
- type: %Schema{type: :string, enum: ["image", "video", "audio", "unknown"]},
- pleroma: %Schema{
- type: :object,
- properties: %{mime_type: %Schema{type: :string}}
- }
- }
- }
+ items: Attachment
},
mentions: %Schema{
type: :array,
diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
index 899b78873..1719c67ea 100644
--- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
@@ -11,17 +11,21 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
alias Pleroma.ScheduledActivity
alias Pleroma.Web.MastodonAPI.MastodonAPI
- plug(:assign_scheduled_activity when action != :index)
-
@oauth_read_actions [:show, :index]
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions)
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions)
+ plug(:assign_scheduled_activity when action != :index)
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ScheduledActivityOperation
+
@doc "GET /api/v1/scheduled_statuses"
def index(%{assigns: %{user: user}} = conn, params) do
+ params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
+
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
conn
|> add_link_headers(scheduled_activities)
@@ -35,7 +39,7 @@ def show(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params)
end
@doc "PUT /api/v1/scheduled_statuses/:id"
- def update(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, params) do
+ def update(%{assigns: %{scheduled_activity: scheduled_activity}, body_params: params} = conn, _) do
with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
render(conn, "show.json", scheduled_activity: scheduled_activity)
end
@@ -48,7 +52,7 @@ def delete(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params
end
end
- defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
+ defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do
case ScheduledActivity.get(user, id) do
%ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity)
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
diff --git a/test/support/helpers.ex b/test/support/helpers.ex
index e68e9bfd2..26281b45e 100644
--- a/test/support/helpers.ex
+++ b/test/support/helpers.ex
@@ -40,12 +40,18 @@ defmacro __using__(_opts) do
clear_config: 2
]
- def to_datetime(naive_datetime) do
+ def to_datetime(%NaiveDateTime{} = naive_datetime) do
naive_datetime
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.truncate(:second)
end
+ def to_datetime(datetime) when is_binary(datetime) do
+ datetime
+ |> NaiveDateTime.from_iso8601!()
+ |> to_datetime()
+ end
+
def collect_ids(collection) do
collection
|> Enum.map(& &1.id)
diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs
index f86274d57..1ff871c89 100644
--- a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs
+++ b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs
@@ -24,19 +24,19 @@ test "shows scheduled activities" do
# min_id
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
- result = json_response(conn_res, 200)
+ result = json_response_and_validate_schema(conn_res, 200)
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
# since_id
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
- result = json_response(conn_res, 200)
+ result = json_response_and_validate_schema(conn_res, 200)
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
# max_id
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
- result = json_response(conn_res, 200)
+ result = json_response_and_validate_schema(conn_res, 200)
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
end
@@ -46,12 +46,12 @@ test "shows a scheduled activity" do
res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}")
- assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
+ assert %{"id" => scheduled_activity_id} = json_response_and_validate_schema(res_conn, 200)
assert scheduled_activity_id == scheduled_activity.id |> to_string()
res_conn = get(conn, "/api/v1/scheduled_statuses/404")
- assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
end
test "updates a scheduled activity" do
@@ -74,22 +74,32 @@ test "updates a scheduled activity" do
assert job.args == %{"activity_id" => scheduled_activity.id}
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at)
- new_scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 120)
+ new_scheduled_at =
+ NaiveDateTime.utc_now()
+ |> Timex.shift(minutes: 120)
+ |> Timex.format!("%Y-%m-%dT%H:%M:%S.%fZ", :strftime)
res_conn =
- put(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> 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 %{"scheduled_at" => expected_scheduled_at} =
+ json_response_and_validate_schema(res_conn, 200)
+
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
job = refresh_record(job)
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at)
- res_conn = put(conn, "/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
+ res_conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
- assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
end
test "deletes a scheduled activity" do
@@ -115,7 +125,7 @@ test "deletes a scheduled activity" do
|> assign(:user, user)
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
- assert %{} = json_response(res_conn, 200)
+ assert %{} = json_response_and_validate_schema(res_conn, 200)
refute Repo.get(ScheduledActivity, scheduled_activity.id)
refute Repo.get(Oban.Job, job.id)
@@ -124,6 +134,6 @@ test "deletes a scheduled activity" do
|> assign(:user, user)
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
- assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
end
end
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index 6791c2fb0..451723e60 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -402,11 +402,17 @@ test "attachments" do
pleroma: %{mime_type: "image/png"}
}
+ api_spec = Pleroma.Web.ApiSpec.spec()
+
assert expected == StatusView.render("attachment.json", %{attachment: object})
+ OpenApiSpex.TestAssertions.assert_schema(expected, "Attachment", api_spec)
# If theres a "id", use that instead of the generated one
object = Map.put(object, "id", 2)
- assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object})
+ result = StatusView.render("attachment.json", %{attachment: object})
+
+ assert %{id: "2"} = result
+ OpenApiSpex.TestAssertions.assert_schema(result, "Attachment", api_spec)
end
test "put the url advertised in the Activity in to the url attribute" do