Add OpenAPI spec for StatusController

This commit is contained in:
Egor Kislitsyn 2020-05-12 23:59:26 +04:00
commit 7803a85d2c
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
71 changed files with 1856 additions and 1082 deletions

View file

@ -844,15 +844,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
end
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
params =
params
|> Map.take(["sensitive", "visibility"])
|> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end)
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
{:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])
ModerationLog.insert_log(%{
action: "status_update",
actor: admin,
subject: activity,
sensitive: sensitive,
visibility: params["visibility"]
visibility: params[:visibility]
})
conn

View file

@ -0,0 +1,502 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.StatusOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
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: ["Statuses"],
summary: "Get multiple statuses by IDs",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [
Operation.parameter(
:ids,
:query,
%Schema{type: :array, items: FlakeID},
"Array of account IDs"
)
],
operationId: "StatusController.index",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
}
}
end
def create_operation do
%Operation{
tags: ["Statuses"],
summary: "Publish new status",
security: [%{"oAuth" => ["write:statuses"]}],
description: "Post a new status",
operationId: "StatusController.create",
requestBody: request_body("Parameters", create_request(), required: true),
responses: %{
200 =>
Operation.response(
"Status. When `scheduled_at` is present, ScheduledStatus is returned instead",
"application/json",
%Schema{oneOf: [Status, ScheduledStatus]}
),
422 => Operation.response("Bad Request", "application/json", ApiError)
}
}
end
def show_operation do
%Operation{
tags: ["Statuses"],
summary: "View specific status",
description: "View information about a status",
operationId: "StatusController.show",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [id_param()],
responses: %{
200 => status_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def delete_operation do
%Operation{
tags: ["Statuses"],
summary: "Delete status",
security: [%{"oAuth" => ["write:statuses"]}],
description: "Delete one of your own statuses",
operationId: "StatusController.delete",
parameters: [id_param()],
responses: %{
200 => empty_object_response(),
403 => Operation.response("Forbidden", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def reblog_operation do
%Operation{
tags: ["Statuses"],
summary: "Boost",
security: [%{"oAuth" => ["write:statuses"]}],
description: "Reshare a status",
operationId: "StatusController.reblog",
parameters: [id_param()],
requestBody:
request_body("Parameters", %Schema{
type: :object,
properties: %{
visibility: %Schema{allOf: [VisibilityScope], default: "public"}
}
}),
responses: %{
200 => status_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def unreblog_operation do
%Operation{
tags: ["Statuses"],
summary: "Undo boost",
security: [%{"oAuth" => ["write:statuses"]}],
description: "Undo a reshare of a status",
operationId: "StatusController.unreblog",
parameters: [id_param()],
responses: %{
200 => status_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def favourite_operation do
%Operation{
tags: ["Statuses"],
summary: "Favourite",
security: [%{"oAuth" => ["write:favourites"]}],
description: "Add a status to your favourites list",
operationId: "StatusController.favourite",
parameters: [id_param()],
responses: %{
200 => status_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def unfavourite_operation do
%Operation{
tags: ["Statuses"],
summary: "Undo favourite",
security: [%{"oAuth" => ["write:favourites"]}],
description: "Remove a status from your favourites list",
operationId: "StatusController.unfavourite",
parameters: [id_param()],
responses: %{
200 => status_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def pin_operation do
%Operation{
tags: ["Statuses"],
summary: "Pin to profile",
security: [%{"oAuth" => ["write:accounts"]}],
description: "Feature one of your own public statuses at the top of your profile",
operationId: "StatusController.pin",
parameters: [id_param()],
responses: %{
200 => status_response(),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end
def unpin_operation do
%Operation{
tags: ["Statuses"],
summary: "Unpin to profile",
security: [%{"oAuth" => ["write:accounts"]}],
description: "Unfeature a status from the top of your profile",
operationId: "StatusController.unpin",
parameters: [id_param()],
responses: %{
200 => status_response(),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end
def bookmark_operation do
%Operation{
tags: ["Statuses"],
summary: "Bookmark",
security: [%{"oAuth" => ["write:bookmarks"]}],
description: "Privately bookmark a status",
operationId: "StatusController.bookmark",
parameters: [id_param()],
responses: %{
200 => status_response()
}
}
end
def unbookmark_operation do
%Operation{
tags: ["Statuses"],
summary: "Undo bookmark",
security: [%{"oAuth" => ["write:bookmarks"]}],
description: "Remove a status from your private bookmarks",
operationId: "StatusController.unbookmark",
parameters: [id_param()],
responses: %{
200 => status_response()
}
}
end
def mute_conversation_operation do
%Operation{
tags: ["Statuses"],
summary: "Mute conversation",
security: [%{"oAuth" => ["write:mutes"]}],
description:
"Do not receive notifications for the thread that this status is part of. Must be a thread in which you are a participant.",
operationId: "StatusController.mute_conversation",
parameters: [id_param()],
responses: %{
200 => status_response(),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end
def unmute_conversation_operation do
%Operation{
tags: ["Statuses"],
summary: "Unmute conversation",
security: [%{"oAuth" => ["write:mutes"]}],
description:
"Start receiving notifications again for the thread that this status is part of",
operationId: "StatusController.unmute_conversation",
parameters: [id_param()],
responses: %{
200 => status_response(),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end
def card_operation do
%Operation{
tags: ["Statuses"],
deprecated: true,
summary: "Preview card",
description: "Deprecated in favor of card property inlined on Status entity",
operationId: "StatusController.card",
parameters: [id_param()],
security: [%{"oAuth" => ["read:statuses"]}],
responses: %{
200 =>
Operation.response("Card", "application/json", %Schema{
type: :object,
nullable: true,
properties: %{
type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]},
provider_name: %Schema{type: :string, nullable: true},
provider_url: %Schema{type: :string, format: :uri},
url: %Schema{type: :string, format: :uri},
image: %Schema{type: :string, nullable: true, format: :uri},
title: %Schema{type: :string},
description: %Schema{type: :string}
}
})
}
}
end
def favourited_by_operation do
%Operation{
tags: ["Statuses"],
summary: "Favourited by",
description: "View who favourited a given status",
operationId: "StatusController.favourited_by",
security: [%{"oAuth" => ["read:accounts"]}],
parameters: [id_param()],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
AccountOperation.array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def reblogged_by_operation do
%Operation{
tags: ["Statuses"],
summary: "Boosted by",
description: "View who boosted a given status",
operationId: "StatusController.reblogged_by",
security: [%{"oAuth" => ["read:accounts"]}],
parameters: [id_param()],
responses: %{
200 =>
Operation.response(
"Array of Accounts",
"application/json",
AccountOperation.array_of_accounts()
),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def context_operation do
%Operation{
tags: ["Statuses"],
summary: "Parent and child statuses",
description: "View statuses above and below this status in the thread",
operationId: "StatusController.context",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [id_param()],
responses: %{
200 => Operation.response("Context", "application/json", context())
}
}
end
def favourites_operation do
%Operation{
tags: ["Statuses"],
summary: "Favourited statuses",
description: "Statuses the user has favourited",
operationId: "StatusController.favourites",
parameters: pagination_params(),
security: [%{"oAuth" => ["read:favourites"]}],
responses: %{
200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
}
}
end
def bookmarks_operation do
%Operation{
tags: ["Statuses"],
summary: "Bookmarked statuses",
description: "Statuses the user has bookmarked",
operationId: "StatusController.bookmarks",
parameters: [
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
| pagination_params()
],
security: [%{"oAuth" => ["read:bookmarks"]}],
responses: %{
200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
}
}
end
defp array_of_statuses do
%Schema{type: :array, items: Status, example: [Status.schema().example]}
end
defp create_request do
%Schema{
title: "StatusCreateRequest",
type: :object,
properties: %{
status: %Schema{
type: :string,
description:
"Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided."
},
media_ids: %Schema{
type: :array,
items: %Schema{type: :string},
description:
"Array of Attachment ids to be attached as media. If provided, `status` becomes optional, and `poll` cannot be used."
},
poll: %Schema{
type: :object,
required: [:options],
properties: %{
options: %Schema{
type: :array,
items: %Schema{type: :string},
description:
"Array of possible answers. If provided, `media_ids` cannot be used, and `poll[expires_in]` must be provided."
},
expires_in: %Schema{
type: :integer,
description:
"Duration the poll should be open, in seconds. If provided, `media_ids` cannot be used, and `poll[options]` must be provided."
},
multiple: %Schema{type: :boolean, description: "Allow multiple choices?"},
hide_totals: %Schema{
type: :boolean,
description: "Hide vote counts until the poll ends?"
}
}
},
in_reply_to_id: %Schema{
allOf: [FlakeID],
description: "ID of the status being replied to, if status is a reply"
},
sensitive: %Schema{
type: :boolean,
description: "Mark status and attached media as sensitive?"
},
spoiler_text: %Schema{
type: :string,
description:
"Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field."
},
scheduled_at: %Schema{
type: :string,
format: :"date-time",
nullable: true,
description:
"ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future."
},
language: %Schema{type: :string, description: "ISO 639 language code for this status."},
# Pleroma-specific properties:
preview: %Schema{
type: :boolean,
description:
"If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
},
content_type: %Schema{
type: :string,
description:
"The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint."
},
to: %Schema{
type: :array,
items: %Schema{type: :string},
description:
"A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply"
},
visibility: %Schema{
anyOf: [
VisibilityScope,
%Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"}
],
description:
"Visibility of the posted status. Besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`"
},
expires_in: %Schema{
type: :integer,
description:
"The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour."
},
in_reply_to_conversation_id: %Schema{
type: :string,
description:
"Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`."
}
},
example: %{
"status" => "What time is it?",
"sensitive" => "false",
"poll" => %{
"options" => ["Cofe", "Adventure"],
"expires_in" => 420
}
}
}
end
defp id_param do
Operation.parameter(:id, :path, FlakeID, "Status ID",
example: "9umDrYheeY451cQnEe",
required: true
)
end
defp status_response do
Operation.response("Status", "application/json", Status)
end
defp context do
%Schema{
title: "StatusContext",
description:
"Represents the tree around a given status. Used for reconstructing threads of statuses.",
type: :object,
required: [:ancestors, :descendants],
properties: %{
ancestors: array_of_statuses(),
descendants: array_of_statuses()
},
example: %{
"ancestors" => [Status.schema().example],
"descendants" => [Status.schema().example]
}
}
end
end

View file

@ -19,60 +19,127 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
description: "Response schema for a status",
type: :object,
properties: %{
account: Account,
account: %Schema{allOf: [Account], description: "The account that authored this status"},
application: %Schema{
description: "The application used to post this status",
type: :object,
properties: %{
name: %Schema{type: :string},
website: %Schema{type: :string, nullable: true, format: :uri}
}
},
bookmarked: %Schema{type: :boolean},
bookmarked: %Schema{type: :boolean, description: "Have you bookmarked this status?"},
card: %Schema{
type: :object,
nullable: true,
description: "Preview card for links included within status content",
required: [:url, :title, :description, :type],
properties: %{
type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]},
provider_name: %Schema{type: :string, nullable: true},
provider_url: %Schema{type: :string, format: :uri},
url: %Schema{type: :string, format: :uri},
image: %Schema{type: :string, nullable: true, format: :uri},
title: %Schema{type: :string},
description: %Schema{type: :string}
type: %Schema{
type: :string,
enum: ["link", "photo", "video", "rich"],
description: "The type of the preview card"
},
provider_name: %Schema{
type: :string,
nullable: true,
description: "The provider of the original resource"
},
provider_url: %Schema{
type: :string,
format: :uri,
description: "A link to the provider of the original resource"
},
url: %Schema{type: :string, format: :uri, description: "Location of linked resource"},
image: %Schema{
type: :string,
nullable: true,
format: :uri,
description: "Preview thumbnail"
},
title: %Schema{type: :string, description: "Title of linked resource"},
description: %Schema{type: :string, description: "Description of preview"}
}
},
content: %Schema{type: :string, format: :html},
created_at: %Schema{type: :string, format: "date-time"},
emojis: %Schema{type: :array, items: Emoji},
favourited: %Schema{type: :boolean},
favourites_count: %Schema{type: :integer},
content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"},
created_at: %Schema{
type: :string,
format: "date-time",
description: "The date when this status was created"
},
emojis: %Schema{
type: :array,
items: Emoji,
description: "Custom emoji to be used when rendering status content"
},
favourited: %Schema{type: :boolean, description: "Have you favourited this status?"},
favourites_count: %Schema{
type: :integer,
description: "How many favourites this status has received"
},
id: FlakeID,
in_reply_to_account_id: %Schema{type: :string, nullable: true},
in_reply_to_id: %Schema{type: :string, nullable: true},
language: %Schema{type: :string, nullable: true},
in_reply_to_account_id: %Schema{
allOf: [FlakeID],
nullable: true,
description: "ID of the account being replied to"
},
in_reply_to_id: %Schema{
allOf: [FlakeID],
nullable: true,
description: "ID of the status being replied"
},
language: %Schema{
type: :string,
nullable: true,
description: "Primary language of this status"
},
media_attachments: %Schema{
type: :array,
items: Attachment
items: Attachment,
description: "Media that is attached to this status"
},
mentions: %Schema{
type: :array,
description: "Mentions of users within the status content",
items: %Schema{
type: :object,
properties: %{
id: %Schema{type: :string},
acct: %Schema{type: :string},
username: %Schema{type: :string},
url: %Schema{type: :string, format: :uri}
id: %Schema{allOf: [FlakeID], description: "The account id of the mentioned user"},
acct: %Schema{
type: :string,
description:
"The webfinger acct: URI of the mentioned user. Equivalent to `username` for local users, or `username@domain` for remote users."
},
username: %Schema{type: :string, description: "The username of the mentioned user"},
url: %Schema{
type: :string,
format: :uri,
description: "The location of the mentioned user's profile"
}
}
}
},
muted: %Schema{type: :boolean},
pinned: %Schema{type: :boolean},
muted: %Schema{
type: :boolean,
description: "Have you muted notifications for this status's conversation?"
},
pinned: %Schema{
type: :boolean,
description: "Have you pinned this status? Only appears if the status is pinnable."
},
pleroma: %Schema{
type: :object,
properties: %{
content: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
conversation_id: %Schema{type: :integer},
content: %Schema{
type: :object,
additionalProperties: %Schema{type: :string},
description:
"A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`"
},
conversation_id: %Schema{
type: :integer,
description: "The ID of the AP context the status is associated with (if any)"
},
direct_conversation_id: %Schema{
type: :integer,
nullable: true,
@ -81,6 +148,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
},
emoji_reactions: %Schema{
type: :array,
description:
"A list with emoji / reaction maps. Contains no information about the reacting users, for that use the /statuses/:id/reactions endpoint.",
items: %Schema{
type: :object,
properties: %{
@ -90,27 +159,74 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
}
}
},
expires_at: %Schema{type: :string, format: "date-time", nullable: true},
in_reply_to_account_acct: %Schema{type: :string, nullable: true},
local: %Schema{type: :boolean},
spoiler_text: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
thread_muted: %Schema{type: :boolean}
expires_at: %Schema{
type: :string,
format: "date-time",
nullable: true,
description:
"A datetime (ISO 8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire"
},
in_reply_to_account_acct: %Schema{
type: :string,
nullable: true,
description: "The `acct` property of User entity for replied user (if any)"
},
local: %Schema{
type: :boolean,
description: "`true` if the post was made on the local instance"
},
spoiler_text: %Schema{
type: :object,
additionalProperties: %Schema{type: :string},
description:
"A map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`."
},
thread_muted: %Schema{
type: :boolean,
description: "`true` if the thread the post belongs to is muted"
}
}
},
poll: %Schema{type: Poll, nullable: true},
poll: %Schema{allOf: [Poll], nullable: true, description: "The poll attached to the status"},
reblog: %Schema{
allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}],
nullable: true
nullable: true,
description: "The status being reblogged"
},
reblogged: %Schema{type: :boolean, description: "Have you boosted this status?"},
reblogs_count: %Schema{
type: :integer,
description: "How many boosts this status has received"
},
replies_count: %Schema{
type: :integer,
description: "How many replies this status has received"
},
sensitive: %Schema{
type: :boolean,
description: "Is this status marked as sensitive content?"
},
spoiler_text: %Schema{
type: :string,
description:
"Subject or summary line, below which status content is collapsed until expanded"
},
reblogged: %Schema{type: :boolean},
reblogs_count: %Schema{type: :integer},
replies_count: %Schema{type: :integer},
sensitive: %Schema{type: :boolean},
spoiler_text: %Schema{type: :string},
tags: %Schema{type: :array, items: Tag},
uri: %Schema{type: :string, format: :uri},
url: %Schema{type: :string, nullable: true, format: :uri},
visibility: VisibilityScope
uri: %Schema{
type: :string,
format: :uri,
description: "URI of the status used for federation"
},
url: %Schema{
type: :string,
nullable: true,
format: :uri,
description: "A link to the status's HTML representation"
},
visibility: %Schema{
allOf: [VisibilityScope],
description: "Visibility of this status"
}
},
example: %{
"account" => %{

View file

@ -9,6 +9,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do
title: "VisibilityScope",
description: "Status visibility",
type: :string,
enum: ["public", "unlisted", "private", "direct"]
enum: ["public", "unlisted", "private", "direct", "list"]
})
end

View file

@ -58,16 +58,16 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
defp put_params(draft, params) do
params = Map.put_new(params, "in_reply_to_status_id", params["in_reply_to_id"])
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
%__MODULE__{draft | params: params}
end
defp status(%{params: %{"status" => status}} = draft) do
defp status(%{params: %{status: status}} = draft) do
%__MODULE__{draft | status: String.trim(status)}
end
defp summary(%{params: params} = draft) do
%__MODULE__{draft | summary: Map.get(params, "spoiler_text", "")}
%__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}
end
defp full_payload(%{status: status, summary: summary} = draft) do
@ -84,20 +84,20 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
%__MODULE__{draft | attachments: attachments}
end
defp in_reply_to(%{params: %{"in_reply_to_status_id" => ""}} = draft), do: draft
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
defp in_reply_to(%{params: %{"in_reply_to_status_id" => id}} = draft) when is_binary(id) do
defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do
%__MODULE__{draft | in_reply_to: Activity.get_by_id(id)}
end
defp in_reply_to(%{params: %{"in_reply_to_status_id" => %Activity{} = in_reply_to}} = draft) do
defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do
%__MODULE__{draft | in_reply_to: in_reply_to}
end
defp in_reply_to(draft), do: draft
defp in_reply_to_conversation(draft) do
in_reply_to_conversation = Participation.get(draft.params["in_reply_to_conversation_id"])
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
end
@ -112,7 +112,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
defp expires_at(draft) do
case CommonAPI.check_expiry_date(draft.params["expires_in"]) do
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
{:error, message} -> add_error(draft, message)
end
@ -144,7 +144,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
addressed_users =
draft.mentions
|> Enum.map(fn {_, mentioned_user} -> mentioned_user.ap_id end)
|> Utils.get_addressed_users(draft.params["to"])
|> Utils.get_addressed_users(draft.params[:to])
{to, cc} =
Utils.get_to_and_cc(
@ -164,7 +164,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
defp sensitive(draft) do
sensitive = draft.params["sensitive"] || Enum.member?(draft.tags, {"#nsfw", "nsfw"})
sensitive = draft.params[:sensitive] || Enum.member?(draft.tags, {"#nsfw", "nsfw"})
%__MODULE__{draft | sensitive: sensitive}
end
@ -191,7 +191,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
defp preview?(draft) do
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"])
preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params[:preview])
%__MODULE__{draft | preview?: preview?}
end

View file

@ -116,19 +116,18 @@ defmodule Pleroma.Web.CommonAPI do
end
def repeat(id, user, params \\ %{}) do
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(id)},
object <- Object.normalize(activity),
announce_activity <- Utils.get_existing_announce(user.ap_id, object),
public <- public_announce?(object, params) do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
object = Object.normalize(activity)
announce_activity = Utils.get_existing_announce(user.ap_id, object)
public = public_announce?(object, params)
if announce_activity do
{:ok, announce_activity, object}
else
ActivityPub.announce(user, object, nil, true, public)
end
else
{:find_activity, _} -> {:error, :not_found}
_ -> {:error, dgettext("errors", "Could not repeat")}
_ -> {:error, :not_found}
end
end
@ -286,7 +285,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
def public_announce?(_, %{"visibility" => visibility})
def public_announce?(_, %{visibility: visibility})
when visibility in ~w{public unlisted private direct},
do: visibility in ~w(public unlisted)
@ -296,11 +295,11 @@ defmodule Pleroma.Web.CommonAPI do
def get_visibility(_, _, %Participation{}), do: {"direct", "direct"}
def get_visibility(%{"visibility" => visibility}, in_reply_to, _)
def get_visibility(%{visibility: visibility}, in_reply_to, _)
when visibility in ~w{public unlisted private direct},
do: {visibility, get_replied_to_visibility(in_reply_to)}
def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to, _) do
def get_visibility(%{visibility: "list:" <> list_id}, in_reply_to, _) do
visibility = {:list, String.to_integer(list_id)}
{visibility, get_replied_to_visibility(in_reply_to)}
end
@ -358,7 +357,7 @@ defmodule Pleroma.Web.CommonAPI do
end
end
def post(user, %{"status" => _} = data) do
def post(user, %{status: _} = data) do
with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do
draft.changes
|> ActivityPub.create(draft.preview?)
@ -467,11 +466,11 @@ defmodule Pleroma.Web.CommonAPI do
end
end
defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do
toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)})
defp toggle_sensitive(activity, %{sensitive: sensitive}) when sensitive in ~w(true false) do
toggle_sensitive(activity, %{sensitive: String.to_existing_atom(sensitive)})
end
defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive})
defp toggle_sensitive(%Activity{object: object} = activity, %{sensitive: sensitive})
when is_boolean(sensitive) do
new_data = Map.put(object.data, "sensitive", sensitive)
@ -485,7 +484,7 @@ defmodule Pleroma.Web.CommonAPI do
defp toggle_sensitive(activity, _), do: {:ok, activity}
defp set_visibility(activity, %{"visibility" => visibility}) do
defp set_visibility(activity, %{visibility: visibility}) do
Utils.update_activity_visibility(activity, visibility)
end

View file

@ -22,11 +22,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
require Logger
require Pleroma.Constants
def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do
def attachments_from_ids(%{media_ids: ids, descriptions: desc}) do
attachments_from_ids_descs(ids, desc)
end
def attachments_from_ids(%{"media_ids" => ids} = _) do
def attachments_from_ids(%{media_ids: ids}) do
attachments_from_ids_no_descs(ids)
end
@ -37,11 +37,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def attachments_from_ids_no_descs(ids) do
Enum.map(ids, fn media_id ->
case Repo.get(Object, media_id) do
%Object{data: data} = _ -> data
%Object{data: data} -> data
_ -> nil
end
end)
|> Enum.filter(& &1)
|> Enum.reject(&is_nil/1)
end
def attachments_from_ids_descs([], _), do: []
@ -51,14 +51,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
Enum.map(ids, fn media_id ->
case Repo.get(Object, media_id) do
%Object{data: data} = _ ->
%Object{data: data} ->
Map.put(data, "name", descs[media_id])
_ ->
nil
end
end)
|> Enum.filter(& &1)
|> Enum.reject(&is_nil/1)
end
@spec get_to_and_cc(
@ -140,7 +140,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> make_poll_data()
end
def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data)
def make_poll_data(%{poll: %{options: options, expires_in: expires_in}} = data)
when is_list(options) do
limits = Pleroma.Config.get([:instance, :poll_limits])
@ -163,7 +163,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> DateTime.add(expires_in)
|> DateTime.to_iso8601()
key = if truthy_param?(data["poll"]["multiple"]), do: "anyOf", else: "oneOf"
key = if truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"
poll = %{"type" => "Question", key => option_notes, "closed" => end_time}
{:ok, {poll, emoji}}
@ -213,7 +213,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Map.get("attachment_links", Config.get([:instance, :attachment_links]))
|> truthy_param?()
content_type = get_content_type(data["content_type"])
content_type = get_content_type(data[:content_type])
options =
if visibility == "direct" && Config.get([:instance, :safe_dm_mentions]) do

View file

@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show])
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
@ -97,12 +98,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.StatusOperation
@doc """
GET `/api/v1/statuses?ids[]=1&ids[]=2`
`ids` query param is required
"""
def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do
def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do
limit = 100
activities =
@ -125,21 +128,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
Creates a scheduled status when `scheduled_at` param is present and it's far enough
"""
def create(
%{assigns: %{user: user}} = conn,
%{"status" => _, "scheduled_at" => scheduled_at} = params
%{
assigns: %{user: user},
body_params: %{status: _, scheduled_at: scheduled_at} = params
} = conn,
_
)
when not is_nil(scheduled_at) do
params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
attrs = %{
params: Map.new(params, fn {key, value} -> {to_string(key), value} end),
scheduled_at: scheduled_at
}
with {:far_enough, true} <- {:far_enough, ScheduledActivity.far_enough?(scheduled_at)},
attrs <- %{"params" => params, "scheduled_at" => scheduled_at},
{:ok, scheduled_activity} <- ScheduledActivity.create(user, attrs) do
conn
|> put_view(ScheduledActivityView)
|> render("show.json", scheduled_activity: scheduled_activity)
else
{:far_enough, _} ->
create(conn, Map.drop(params, ["scheduled_at"]))
params = Map.drop(params, [:scheduled_at])
create(%Plug.Conn{conn | body_params: params}, %{})
error ->
error
@ -151,8 +162,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
Creates a regular status
"""
def create(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do
params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])
with {:ok, activity} <- CommonAPI.post(user, params) do
try_render(conn, "show.json",
@ -169,12 +180,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
end
def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do
create(conn, Map.put(params, "status", ""))
def create(%{assigns: %{user: _user}, body_params: %{media_ids: _} = params} = conn, _) do
params = Map.put(params, :status, "")
create(%Plug.Conn{conn | body_params: params}, %{})
end
@doc "GET /api/v1/statuses/:id"
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
true <- Visibility.visible_for_user?(activity, user) do
try_render(conn, "show.json",
@ -188,7 +200,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "DELETE /api/v1/statuses/:id"
def delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
json(conn, %{})
else
@ -198,7 +210,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/reblog"
def reblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id} = params) do
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),
%Activity{} = announce <- Activity.normalize(announce.data) do
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
@ -206,7 +218,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/unreblog"
def unreblog(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
def unreblog(%{assigns: %{user: user}} = conn, %{id: activity_id}) do
with {:ok, _unannounce} <- CommonAPI.unrepeat(activity_id, user),
%Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", %{activity: activity, for: user, as: :activity})
@ -214,7 +226,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/favourite"
def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
def favourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do
with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),
%Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
@ -222,7 +234,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/unfavourite"
def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
def unfavourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do
with {:ok, _unfav} <- CommonAPI.unfavorite(activity_id, user),
%Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
@ -230,21 +242,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/pin"
def pin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
def pin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do
with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
end
end
@doc "POST /api/v1/statuses/:id/unpin"
def unpin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
def unpin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do
with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
end
end
@doc "POST /api/v1/statuses/:id/bookmark"
def bookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def bookmark(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
%User{} = user <- User.get_cached_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
@ -254,7 +266,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/unbookmark"
def unbookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def unbookmark(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
%User{} = user <- User.get_cached_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
@ -264,7 +276,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/mute"
def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id(id),
{:ok, activity} <- CommonAPI.add_mute(user, activity) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
@ -272,7 +284,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "POST /api/v1/statuses/:id/unmute"
def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def unmute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id(id),
{:ok, activity} <- CommonAPI.remove_mute(user, activity) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
@ -281,7 +293,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
@doc "GET /api/v1/statuses/:id/card"
@deprecated "https://github.com/tootsuite/mastodon/pull/11213"
def card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
def card(%{assigns: %{user: user}} = conn, %{id: status_id}) do
with %Activity{} = activity <- Activity.get_by_id(status_id),
true <- Visibility.visible_for_user?(activity, user) do
data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
@ -292,7 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "GET /api/v1/statuses/:id/favourited_by"
def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def favourited_by(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
@ -312,7 +324,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "GET /api/v1/statuses/:id/reblogged_by"
def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def reblogged_by(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
%Object{data: %{"announcements" => announces, "id" => ap_id}} <-
@ -344,7 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
@doc "GET /api/v1/statuses/:id/context"
def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
def context(%{assigns: %{user: user}} = conn, %{id: id}) do
with %Activity{} = activity <- Activity.get_by_id(id) do
activities =
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
@ -359,11 +371,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
@doc "GET /api/v1/favourites"
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
activities =
ActivityPub.fetch_favourites(
user,
Map.take(params, Pleroma.Pagination.page_keys())
)
params =
params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.take(Pleroma.Pagination.page_keys())
activities = ActivityPub.fetch_favourites(user, params)
conn
|> add_link_headers(activities)

View file

@ -337,7 +337,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_role(data, _, _), do: data
defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings)
Kernel.put_in(
data,
[:pleroma, :notification_settings],
Map.from_struct(user.notification_settings)
)
end
defp maybe_put_notification_settings(data, _, _), do: data