Merge remote-tracking branch 'remotes/origin/develop' into automatic-authentication-and-instance-publicity-checks
# Conflicts: # lib/pleroma/web/mastodon_api/controllers/account_controller.ex
This commit is contained in:
commit
908cf22a6c
@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
<summary>API Changes</summary>
|
||||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
||||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
||||
- Mastodon API: Add support for filtering replies in public and home timelines
|
||||
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||
</details>
|
||||
|
||||
|
@ -279,7 +279,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
||||
actor = get_actor(group, user, friends, non_friends)
|
||||
|
||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||
{:ok, _activity, _object} <- CommonAPI.favorite(activity_id, actor) do
|
||||
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
||||
:ok
|
||||
else
|
||||
{:error, _} ->
|
||||
@ -313,7 +313,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
||||
tasks = get_reply_tasks(visibility, group)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{"status" => "Simple status", "visibility" => "unlisted"})
|
||||
CommonAPI.post(user, %{"status" => "Simple status", "visibility" => visibility})
|
||||
|
||||
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
||||
insert_replies(tasks, visibility, user, friends, non_friends, acc)
|
||||
|
@ -41,6 +41,7 @@ defmodule Pleroma.LoadTesting.Fetcher do
|
||||
fetch_notifications(user)
|
||||
fetch_favourites(user)
|
||||
fetch_long_thread(user)
|
||||
fetch_timelines_with_reply_filtering(user)
|
||||
end
|
||||
|
||||
defp render_views(user) do
|
||||
@ -495,4 +496,58 @@ defmodule Pleroma.LoadTesting.Fetcher do
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp fetch_timelines_with_reply_filtering(user) do
|
||||
public_params = opts_for_public_timeline(user)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Public timeline without reply filtering" => fn ->
|
||||
ActivityPub.fetch_public_activities(public_params)
|
||||
end,
|
||||
"Public timeline with reply filtering - following" => fn ->
|
||||
public_params
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
end,
|
||||
"Public timeline with reply filtering - self" => fn ->
|
||||
public_params
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
|
||||
private_params = opts_for_home_timeline(user)
|
||||
|
||||
recipients = [user.ap_id | User.following(user)]
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Home timeline without reply filtering" => fn ->
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end,
|
||||
"Home timeline with reply filtering - following" => fn ->
|
||||
private_params =
|
||||
private_params
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end,
|
||||
"Home timeline with reply filtering - self" => fn ->
|
||||
private_params =
|
||||
private_params
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -44,6 +44,7 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do
|
||||
]
|
||||
|
||||
def run(args) do
|
||||
Logger.configure(level: :error)
|
||||
Mix.Pleroma.start_pleroma()
|
||||
clean_tables()
|
||||
{opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
|
||||
|
@ -4,7 +4,7 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
|
||||
|
||||
## Flake IDs
|
||||
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
||||
|
||||
## Attachment cap
|
||||
|
||||
@ -14,6 +14,7 @@ Some apps operate under the assumption that no more than 4 attachments can be re
|
||||
|
||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
||||
|
||||
## Statuses
|
||||
|
||||
@ -119,6 +120,18 @@ Accepts additional parameters:
|
||||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
|
||||
## DELETE `/api/v1/notifications/destroy_multiple`
|
||||
|
||||
An endpoint to delete multiple statuses by IDs.
|
||||
|
||||
Required parameters:
|
||||
|
||||
- `ids`: array of activity ids
|
||||
|
||||
Usage example: `DELETE /api/v1/notifications/destroy_multiple/?ids[]=1&ids[]=2`.
|
||||
|
||||
Returns on success: 200 OK `{}`
|
||||
|
||||
## POST `/api/v1/statuses`
|
||||
|
||||
Additional parameters can be added to the JSON body/Form data:
|
||||
|
@ -36,7 +36,7 @@ content-security-policy:
|
||||
default-src 'none';
|
||||
base-uri 'self';
|
||||
frame-ancestors 'none';
|
||||
img-src 'self' data: https:;
|
||||
img-src 'self' data: blob: https:;
|
||||
media-src 'self' https:;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
font-src 'self';
|
||||
|
@ -75,7 +75,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
|
||||
"default-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"img-src 'self' data: https:",
|
||||
"img-src 'self' data: blob: https:",
|
||||
"media-src 'self' https:",
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
"font-src 'self'",
|
||||
|
@ -45,11 +45,11 @@ defmodule Pleroma.Stats do
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
{:ok, get_stat_data()}
|
||||
{:ok, calculate_stat_data()}
|
||||
end
|
||||
|
||||
def handle_call(:force_update, _from, _state) do
|
||||
new_stats = get_stat_data()
|
||||
new_stats = calculate_stat_data()
|
||||
{:reply, new_stats, new_stats}
|
||||
end
|
||||
|
||||
@ -58,12 +58,12 @@ defmodule Pleroma.Stats do
|
||||
end
|
||||
|
||||
def handle_cast(:run_update, _state) do
|
||||
new_stats = get_stat_data()
|
||||
new_stats = calculate_stat_data()
|
||||
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
defp get_stat_data do
|
||||
def calculate_stat_data do
|
||||
peers =
|
||||
from(
|
||||
u in User,
|
||||
@ -77,7 +77,15 @@ defmodule Pleroma.Stats do
|
||||
|
||||
status_count = Repo.aggregate(User.Query.build(%{local: true}), :sum, :note_count)
|
||||
|
||||
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)
|
||||
users_query =
|
||||
from(u in User,
|
||||
where: u.deactivated != true,
|
||||
where: u.local == true,
|
||||
where: not is_nil(u.nickname),
|
||||
where: not u.invisible
|
||||
)
|
||||
|
||||
user_count = Repo.aggregate(users_query, :count, :id)
|
||||
|
||||
%{
|
||||
peers: peers,
|
||||
|
@ -832,6 +832,7 @@ defmodule Pleroma.User do
|
||||
def set_cache(%User{} = user) do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
Cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
@ -847,9 +848,22 @@ defmodule Pleroma.User do
|
||||
end
|
||||
end
|
||||
|
||||
def get_user_friends_ap_ids(user) do
|
||||
from(u in User.get_friends_query(user), select: u.ap_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()]
|
||||
def get_cached_user_friends_ap_ids(user) do
|
||||
Cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
|
||||
get_user_friends_ap_ids(user)
|
||||
end)
|
||||
end
|
||||
|
||||
def invalidate_cache(user) do
|
||||
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
||||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||
Cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
|
||||
end
|
||||
|
||||
@spec get_cached_by_ap_id(String.t()) :: User.t() | nil
|
||||
|
@ -54,13 +54,13 @@ defmodule Pleroma.User.Query do
|
||||
select: term(),
|
||||
limit: pos_integer()
|
||||
}
|
||||
| %{}
|
||||
| map()
|
||||
|
||||
@ilike_criteria [:nickname, :name, :query]
|
||||
@equal_criteria [:email]
|
||||
@contains_criteria [:ap_id, :nickname]
|
||||
|
||||
@spec build(criteria()) :: Query.t()
|
||||
@spec build(Query.t(), criteria()) :: Query.t()
|
||||
def build(query \\ base_query(), criteria) do
|
||||
prepare_query(query, criteria)
|
||||
end
|
||||
|
@ -398,36 +398,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
||||
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def like(user, object, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_like(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
activity_id,
|
||||
local
|
||||
) do
|
||||
with nil <- get_existing_like(ap_id, object),
|
||||
like_data <- make_like_data(user, object, activity_id),
|
||||
{:ok, activity} <- insert(like_data, local),
|
||||
{:ok, object} <- add_like_to_object(activity, object),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
%Activity{} = activity ->
|
||||
{:ok, activity, object}
|
||||
|
||||
{:error, error} ->
|
||||
Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
|
||||
def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
|
||||
@ -468,6 +438,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp do_announce(user, object, activity_id, local, public) do
|
||||
with true <- is_announceable?(object, user, public),
|
||||
object <- Object.get_by_id(object.id),
|
||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
||||
{:ok, activity} <- insert(announce_data, local),
|
||||
{:ok, object} <- add_announce_to_object(activity, object),
|
||||
@ -854,7 +825,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
|
||||
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||
when visibility not in @valid_visibilities do
|
||||
when visibility not in [nil | @valid_visibilities] do
|
||||
Logger.error("Could not exclude visibility to #{visibility}")
|
||||
query
|
||||
end
|
||||
@ -1061,7 +1032,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
raise "Can't use the child object without preloading!"
|
||||
end
|
||||
|
||||
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
|
||||
from(
|
||||
[_activity, object] in query,
|
||||
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
||||
@ -1070,16 +1041,51 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp restrict_media(query, _), do: query
|
||||
|
||||
defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do
|
||||
from(
|
||||
[_activity, object] in query,
|
||||
where: fragment("?->>'inReplyTo' is null", object.data)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_replies(query, %{
|
||||
"reply_filtering_user" => user,
|
||||
"reply_visibility" => "self"
|
||||
}) do
|
||||
from(
|
||||
[activity, object] in query,
|
||||
where:
|
||||
fragment(
|
||||
"?->>'inReplyTo' is null OR ? = ANY(?)",
|
||||
object.data,
|
||||
^user.ap_id,
|
||||
activity.recipients
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_replies(query, %{
|
||||
"reply_filtering_user" => user,
|
||||
"reply_visibility" => "following"
|
||||
}) do
|
||||
from(
|
||||
[activity, object] in query,
|
||||
where:
|
||||
fragment(
|
||||
"?->>'inReplyTo' is null OR ? && array_remove(?, ?) OR ? = ?",
|
||||
object.data,
|
||||
^[user.ap_id | User.get_cached_user_friends_ap_ids(user)],
|
||||
activity.recipients,
|
||||
activity.actor,
|
||||
activity.actor,
|
||||
^user.ap_id
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_replies(query, _), do: query
|
||||
|
||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do
|
||||
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
||||
end
|
||||
|
||||
@ -1158,7 +1164,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
|
||||
# TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
|
||||
# the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
|
||||
# and `restrict_muted/2`
|
||||
|
||||
defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
|
||||
when pinned in [true, "true", "1"] do
|
||||
from(activity in query, where: activity.id in ^ids)
|
||||
end
|
||||
|
||||
@ -1291,6 +1302,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> maybe_set_thread_muted_field(opts)
|
||||
|> maybe_order(opts)
|
||||
|> restrict_recipients(recipients, opts["user"])
|
||||
|> restrict_replies(opts)
|
||||
|> restrict_tag(opts)
|
||||
|> restrict_tag_reject(opts)
|
||||
|> restrict_tag_all(opts)
|
||||
@ -1305,7 +1317,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> restrict_media(opts)
|
||||
|> restrict_visibility(opts)
|
||||
|> restrict_thread_visibility(opts, config)
|
||||
|> restrict_replies(opts)
|
||||
|> restrict_reblogs(opts)
|
||||
|> restrict_pinned(opts)
|
||||
|> restrict_muted_reblogs(restrict_muted_reblogs_opts)
|
||||
|
@ -12,8 +12,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
alias Pleroma.Plugs.EnsureAuthenticatedPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Relay
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
@ -421,7 +423,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"]),
|
||||
{:ok, activity, _object} <- ActivityPub.like(user, object) do
|
||||
{_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
|
||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
|
||||
{:ok, activity}
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Can't like object")}
|
||||
|
@ -15,12 +15,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Add like to object
|
||||
# - Set up notification
|
||||
def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
||||
liked_object = Object.get_by_ap_id(object.data["object"])
|
||||
Utils.add_like_to_object(object, liked_object)
|
||||
{:ok, result} =
|
||||
Pleroma.Repo.transaction(fn ->
|
||||
liked_object = Object.get_by_ap_id(object.data["object"])
|
||||
Utils.add_like_to_object(object, liked_object)
|
||||
|
||||
Notification.create_notifications(object)
|
||||
Notification.create_notifications(object)
|
||||
|
||||
{:ok, object, meta}
|
||||
{:ok, object, meta}
|
||||
end)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Nothing to do
|
||||
|
@ -8,15 +8,16 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
def render("index.json", opts) do
|
||||
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
user = get_user(activity.data["actor"])
|
||||
user = StatusView.get_user(activity.data["actor"])
|
||||
|
||||
Pleroma.Web.MastodonAPI.StatusView.render("show.json", opts)
|
||||
StatusView.render("show.json", opts)
|
||||
|> Map.merge(%{account: merge_account_views(user)})
|
||||
end
|
||||
|
||||
@ -26,17 +27,4 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||
end
|
||||
|
||||
defp merge_account_views(_), do: %{}
|
||||
|
||||
defp get_user(ap_id) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
true ->
|
||||
User.error_user(ap_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec do
|
||||
alias OpenApiSpex.OpenApi
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
@ -24,6 +25,13 @@ defmodule Pleroma.Web.ApiSpec do
|
||||
# populate the paths from a phoenix router
|
||||
paths: OpenApiSpex.Paths.from_router(Router),
|
||||
components: %OpenApiSpex.Components{
|
||||
parameters: %{
|
||||
"accountIdOrNickname" =>
|
||||
Operation.parameter(:id, :path, :string, "Account ID or nickname",
|
||||
example: "123",
|
||||
required: true
|
||||
)
|
||||
},
|
||||
securitySchemes: %{
|
||||
"oAuth" => %OpenApiSpex.SecurityScheme{
|
||||
type: "oauth2",
|
||||
|
@ -3,6 +3,9 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Helpers do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def request_body(description, schema_ref, opts \\ []) do
|
||||
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
|
||||
|
||||
@ -24,4 +27,23 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
|
||||
required: opts[:required] || false
|
||||
}
|
||||
end
|
||||
|
||||
def pagination_params do
|
||||
[
|
||||
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
|
||||
Operation.parameter(:min_id, :query, :string, "Return the oldest items newer than this ID"),
|
||||
Operation.parameter(
|
||||
:since_id,
|
||||
:query,
|
||||
:string,
|
||||
"Return the newest items newer than this ID"
|
||||
),
|
||||
Operation.parameter(
|
||||
:limit,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 20, maximum: 40},
|
||||
"Limit"
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
701
lib/pleroma/web/api_spec/operations/account_operation.ex
Normal file
701
lib/pleroma/web/api_spec/operations/account_operation.ex
Normal file
@ -0,0 +1,701 @@
|
||||
# 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.AccountOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Reference
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
@spec create_operation() :: Operation.t()
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Register an account",
|
||||
description:
|
||||
"Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.",
|
||||
operationId: "AccountController.create",
|
||||
requestBody: request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", create_response()),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
403 => Operation.response("Error", "application/json", ApiError),
|
||||
429 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def verify_credentials_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
description: "Test to make sure that the user token works.",
|
||||
summary: "Verify account credentials",
|
||||
operationId: "AccountController.verify_credentials",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_credentials_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Update account credentials",
|
||||
description: "Update the user's display and preferences.",
|
||||
operationId: "AccountController.update_credentials",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
requestBody: request_body("Parameters", update_creadentials_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account),
|
||||
403 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def relationships_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Check relationships to other accounts",
|
||||
operationId: "AccountController.relationships",
|
||||
description: "Find out whether a given account is followed, blocked, muted, etc.",
|
||||
security: [%{"oAuth" => ["read:follows"]}],
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:id,
|
||||
:query,
|
||||
%Schema{
|
||||
oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}]
|
||||
},
|
||||
"Account IDs",
|
||||
example: "123"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", array_of_relationships())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Account",
|
||||
operationId: "AccountController.show",
|
||||
description: "View information about a profile.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def statuses_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Statuses",
|
||||
operationId: "AccountController.statuses",
|
||||
description:
|
||||
"Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)",
|
||||
parameters:
|
||||
[
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
|
||||
Operation.parameter(:tagged, :query, :string, "With tag"),
|
||||
Operation.parameter(
|
||||
:only_media,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Include only statuses with media attached"
|
||||
),
|
||||
Operation.parameter(
|
||||
:with_muted,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Include statuses from muted acccounts."
|
||||
),
|
||||
Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
|
||||
Operation.parameter(
|
||||
:exclude_visibilities,
|
||||
:query,
|
||||
%Schema{type: :array, items: VisibilityScope},
|
||||
"Exclude visibilities"
|
||||
)
|
||||
] ++ pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Statuses", "application/json", array_of_statuses()),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def followers_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Followers",
|
||||
operationId: "AccountController.followers",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
description:
|
||||
"Accounts which follow the given account, if network is not hidden by the account owner.",
|
||||
parameters:
|
||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def following_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Following",
|
||||
operationId: "AccountController.following",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
description:
|
||||
"Accounts which the given account is following, if network is not hidden by the account owner.",
|
||||
parameters:
|
||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
||||
responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
|
||||
}
|
||||
end
|
||||
|
||||
def lists_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Lists containing this account",
|
||||
operationId: "AccountController.lists",
|
||||
security: [%{"oAuth" => ["read:lists"]}],
|
||||
description: "User lists that you have added this account to.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
|
||||
}
|
||||
end
|
||||
|
||||
def follow_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Follow",
|
||||
operationId: "AccountController.follow",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
description: "Follow the given account",
|
||||
parameters: [
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(
|
||||
:reblogs,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Receive this account's reblogs in home timeline? Defaults to true."
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unfollow_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unfollow",
|
||||
operationId: "AccountController.unfollow",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
description: "Unfollow the given account",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def mute_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Mute",
|
||||
operationId: "AccountController.mute",
|
||||
security: [%{"oAuth" => ["follow", "write:mutes"]}],
|
||||
requestBody: request_body("Parameters", mute_request()),
|
||||
description:
|
||||
"Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).",
|
||||
parameters: [
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(
|
||||
:notifications,
|
||||
:query,
|
||||
%Schema{allOf: [BooleanLike], default: true},
|
||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unmute_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unmute",
|
||||
operationId: "AccountController.unmute",
|
||||
security: [%{"oAuth" => ["follow", "write:mutes"]}],
|
||||
description: "Unmute the given account.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def block_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Block",
|
||||
operationId: "AccountController.block",
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
description:
|
||||
"Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unblock_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unblock",
|
||||
operationId: "AccountController.unblock",
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
description: "Unblock the given account.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def follows_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Follows",
|
||||
operationId: "AccountController.follows",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
requestBody: request_body("Parameters", follows_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def mutes_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Muted accounts",
|
||||
operationId: "AccountController.mutes",
|
||||
description: "Accounts the user has muted.",
|
||||
security: [%{"oAuth" => ["follow", "read:mutes"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def blocks_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Blocked users",
|
||||
operationId: "AccountController.blocks",
|
||||
description: "View your blocks. See also accounts/:id/{block,unblock}",
|
||||
security: [%{"oAuth" => ["read:blocks"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def endorsements_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Endorsements",
|
||||
operationId: "AccountController.endorsements",
|
||||
description: "Not implemented",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empry array", "application/json", %Schema{type: :array})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def identity_proofs_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Identity proofs",
|
||||
operationId: "AccountController.identity_proofs",
|
||||
description: "Not implemented",
|
||||
responses: %{
|
||||
200 => Operation.response("Empry array", "application/json", %Schema{type: :array})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_request do
|
||||
%Schema{
|
||||
title: "AccountCreateRequest",
|
||||
description: "POST body for creating an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
reason: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Text that will be reviewed by moderators if registrations require manual approval"
|
||||
},
|
||||
username: %Schema{type: :string, description: "The desired username for the account"},
|
||||
email: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"The email address to be used for login. Required when `account_activation_required` is enabled.",
|
||||
format: :email
|
||||
},
|
||||
password: %Schema{
|
||||
type: :string,
|
||||
description: "The password to be used for login",
|
||||
format: :password
|
||||
},
|
||||
agreement: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE."
|
||||
},
|
||||
locale: %Schema{
|
||||
type: :string,
|
||||
description: "The language of the confirmation email that will be sent"
|
||||
},
|
||||
# Pleroma-specific properties:
|
||||
fullname: %Schema{type: :string, description: "Full name"},
|
||||
bio: %Schema{type: :string, description: "Bio", default: ""},
|
||||
captcha_solution: %Schema{
|
||||
type: :string,
|
||||
description: "Provider-specific captcha solution"
|
||||
},
|
||||
captcha_token: %Schema{type: :string, description: "Provider-specific captcha token"},
|
||||
captcha_answer_data: %Schema{type: :string, description: "Provider-specific captcha data"},
|
||||
token: %Schema{
|
||||
type: :string,
|
||||
description: "Invite token required when the registrations aren't public"
|
||||
}
|
||||
},
|
||||
required: [:username, :password, :agreement],
|
||||
example: %{
|
||||
"username" => "cofe",
|
||||
"email" => "cofe@example.com",
|
||||
"password" => "secret",
|
||||
"agreement" => "true",
|
||||
"bio" => "☕️"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_response do
|
||||
%Schema{
|
||||
title: "AccountCreateResponse",
|
||||
description: "Response schema for an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
token_type: %Schema{type: :string},
|
||||
access_token: %Schema{type: :string},
|
||||
scope: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
created_at: %Schema{type: :integer, format: :"date-time"}
|
||||
},
|
||||
example: %{
|
||||
"access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
|
||||
"created_at" => 1_585_918_714,
|
||||
"scope" => ["read", "write", "follow", "push"],
|
||||
"token_type" => "Bearer"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp update_creadentials_request do
|
||||
%Schema{
|
||||
title: "AccountUpdateCredentialsRequest",
|
||||
description: "POST body for creating an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
bot: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether the account has a bot flag."
|
||||
},
|
||||
display_name: %Schema{
|
||||
type: :string,
|
||||
description: "The display name to use for the profile."
|
||||
},
|
||||
note: %Schema{type: :string, description: "The account bio."},
|
||||
avatar: %Schema{
|
||||
type: :string,
|
||||
description: "Avatar image encoded using multipart/form-data",
|
||||
format: :binary
|
||||
},
|
||||
header: %Schema{
|
||||
type: :string,
|
||||
description: "Header image encoded using multipart/form-data",
|
||||
format: :binary
|
||||
},
|
||||
locked: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether manual approval of follow requests is required."
|
||||
},
|
||||
fields_attributes: %Schema{
|
||||
oneOf: [
|
||||
%Schema{type: :array, items: attribute_field()},
|
||||
%Schema{type: :object, additionalProperties: %Schema{type: attribute_field()}}
|
||||
]
|
||||
},
|
||||
# NOTE: `source` field is not supported
|
||||
#
|
||||
# source: %Schema{
|
||||
# type: :object,
|
||||
# properties: %{
|
||||
# privacy: %Schema{type: :string},
|
||||
# sensitive: %Schema{type: :boolean},
|
||||
# language: %Schema{type: :string}
|
||||
# }
|
||||
# },
|
||||
|
||||
# Pleroma-specific fields
|
||||
no_rich_text: %Schema{
|
||||
type: :boolean,
|
||||
description: "html tags are stripped from all statuses requested from the API"
|
||||
},
|
||||
hide_followers: %Schema{type: :boolean, description: "user's followers will be hidden"},
|
||||
hide_follows: %Schema{type: :boolean, description: "user's follows will be hidden"},
|
||||
hide_followers_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's follower count will be hidden"
|
||||
},
|
||||
hide_follows_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's follow count will be hidden"
|
||||
},
|
||||
hide_favorites: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's favorites timeline will be hidden"
|
||||
},
|
||||
show_role: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's role (e.g admin, moderator) will be exposed to anyone in the
|
||||
API"
|
||||
},
|
||||
default_scope: VisibilityScope,
|
||||
pleroma_settings_store: %Schema{
|
||||
type: :object,
|
||||
description: "Opaque user settings to be saved on the backend."
|
||||
},
|
||||
skip_thread_containment: %Schema{
|
||||
type: :boolean,
|
||||
description: "Skip filtering out broken threads"
|
||||
},
|
||||
allow_following_move: %Schema{
|
||||
type: :boolean,
|
||||
description: "Allows automatically follow moved following accounts"
|
||||
},
|
||||
pleroma_background_image: %Schema{
|
||||
type: :string,
|
||||
description: "Sets the background image of the user.",
|
||||
format: :binary
|
||||
},
|
||||
discoverable: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"Discovery of this account in search results and other services is allowed."
|
||||
},
|
||||
actor_type: ActorType
|
||||
},
|
||||
example: %{
|
||||
bot: false,
|
||||
display_name: "cofe",
|
||||
note: "foobar",
|
||||
fields_attributes: [%{name: "foo", value: "bar"}],
|
||||
no_rich_text: false,
|
||||
hide_followers: true,
|
||||
hide_follows: false,
|
||||
hide_followers_count: false,
|
||||
hide_follows_count: false,
|
||||
hide_favorites: false,
|
||||
show_role: false,
|
||||
default_scope: "private",
|
||||
pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
|
||||
skip_thread_containment: false,
|
||||
allow_following_move: false,
|
||||
discoverable: false,
|
||||
actor_type: "Person"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_accounts do
|
||||
%Schema{
|
||||
title: "ArrayOfAccounts",
|
||||
type: :array,
|
||||
items: Account
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_relationships do
|
||||
%Schema{
|
||||
title: "ArrayOfRelationships",
|
||||
description: "Response schema for account relationships",
|
||||
type: :array,
|
||||
items: AccountRelationship,
|
||||
example: [
|
||||
%{
|
||||
"id" => "1",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => false,
|
||||
"blocked_by" => true,
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"endorsed" => true
|
||||
},
|
||||
%{
|
||||
"id" => "2",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => false,
|
||||
"blocked_by" => true,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"requested" => true,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"endorsed" => false
|
||||
},
|
||||
%{
|
||||
"id" => "3",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => true,
|
||||
"blocked_by" => false,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"domain_blocking" => true,
|
||||
"subscribing" => true,
|
||||
"endorsed" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp follows_request do
|
||||
%Schema{
|
||||
title: "AccountFollowsRequest",
|
||||
description: "POST body for muting an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
uri: %Schema{type: :string, format: :uri}
|
||||
},
|
||||
required: [:uri]
|
||||
}
|
||||
end
|
||||
|
||||
defp mute_request do
|
||||
%Schema{
|
||||
title: "AccountMuteRequest",
|
||||
description: "POST body for muting an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
notifications: %Schema{
|
||||
type: :boolean,
|
||||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"notifications" => true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp list do
|
||||
%Schema{
|
||||
title: "List",
|
||||
description: "Response schema for a list",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
title: %Schema{type: :string}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"title" => "my list"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_lists do
|
||||
%Schema{
|
||||
title: "ArrayOfLists",
|
||||
description: "Response schema for lists",
|
||||
type: :array,
|
||||
items: list(),
|
||||
example: [
|
||||
%{"id" => "123", "title" => "my list"},
|
||||
%{"id" => "1337", "title" => "anotehr list"}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_statuses do
|
||||
%Schema{
|
||||
title: "ArrayOfStatuses",
|
||||
type: :array,
|
||||
items: Status
|
||||
}
|
||||
end
|
||||
|
||||
defp attribute_field do
|
||||
%Schema{
|
||||
title: "AccountAttributeField",
|
||||
description: "Request schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
value: %Schema{type: :string}
|
||||
},
|
||||
required: [:name, :value],
|
||||
example: %{
|
||||
"name" => "Website",
|
||||
"value" => "https://pleroma.com"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
@ -6,8 +6,6 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Helpers
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
@ -22,9 +20,9 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||
summary: "Create an application",
|
||||
description: "Create a new application to obtain OAuth2 credentials",
|
||||
operationId: "AppController.create",
|
||||
requestBody: Helpers.request_body("Parameters", AppCreateRequest, required: true),
|
||||
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("App", "application/json", AppCreateResponse),
|
||||
200 => Operation.response("App", "application/json", create_response()),
|
||||
422 =>
|
||||
Operation.response(
|
||||
"Unprocessable Entity",
|
||||
@ -51,11 +49,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||
summary: "Verify your app works",
|
||||
description: "Confirm that the app's OAuth2 credentials work.",
|
||||
operationId: "AppController.verify_credentials",
|
||||
security: [
|
||||
%{
|
||||
"oAuth" => ["read"]
|
||||
}
|
||||
],
|
||||
security: [%{"oAuth" => ["read"]}],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("App", "application/json", %Schema{
|
||||
@ -93,4 +87,58 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_request do
|
||||
%Schema{
|
||||
title: "AppCreateRequest",
|
||||
description: "POST body for creating an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
client_name: %Schema{type: :string, description: "A name for your application."},
|
||||
redirect_uris: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||
},
|
||||
scopes: %Schema{
|
||||
type: :string,
|
||||
description: "Space separated list of scopes",
|
||||
default: "read"
|
||||
},
|
||||
website: %Schema{type: :string, description: "A URL to the homepage of your app"}
|
||||
},
|
||||
required: [:client_name, :redirect_uris],
|
||||
example: %{
|
||||
"client_name" => "My App",
|
||||
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_response do
|
||||
%Schema{
|
||||
title: "AppCreateResponse",
|
||||
description: "Response schema for an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
name: %Schema{type: :string},
|
||||
client_id: %Schema{type: :string},
|
||||
client_secret: %Schema{type: :string},
|
||||
redirect_uri: %Schema{type: :string},
|
||||
vapid_key: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"name" => "My App",
|
||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||
"vapid_key" =>
|
||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
@ -18,7 +19,69 @@ defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do
|
||||
description: "Returns custom emojis that are available on the server.",
|
||||
operationId: "CustomEmojiController.index",
|
||||
responses: %{
|
||||
200 => Operation.response("Custom Emojis", "application/json", CustomEmojisResponse)
|
||||
200 => Operation.response("Custom Emojis", "application/json", resposnse())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp resposnse do
|
||||
%Schema{
|
||||
title: "CustomEmojisResponse",
|
||||
description: "Response schema for custom emojis",
|
||||
type: :array,
|
||||
items: custom_emoji(),
|
||||
example: [
|
||||
%{
|
||||
"category" => "Fun",
|
||||
"shortcode" => "blank",
|
||||
"static_url" => "https://lain.com/emoji/blank.png",
|
||||
"tags" => ["Fun"],
|
||||
"url" => "https://lain.com/emoji/blank.png",
|
||||
"visible_in_picker" => false
|
||||
},
|
||||
%{
|
||||
"category" => "Gif,Fun",
|
||||
"shortcode" => "firefox",
|
||||
"static_url" => "https://lain.com/emoji/Firefox.gif",
|
||||
"tags" => ["Gif", "Fun"],
|
||||
"url" => "https://lain.com/emoji/Firefox.gif",
|
||||
"visible_in_picker" => true
|
||||
},
|
||||
%{
|
||||
"category" => "pack:mixed",
|
||||
"shortcode" => "sadcat",
|
||||
"static_url" => "https://lain.com/emoji/mixed/sadcat.png",
|
||||
"tags" => ["pack:mixed"],
|
||||
"url" => "https://lain.com/emoji/mixed/sadcat.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp custom_emoji do
|
||||
%Schema{
|
||||
title: "CustomEmoji",
|
||||
description: "Schema for a CustomEmoji",
|
||||
allOf: [
|
||||
Emoji,
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
category: %Schema{type: :string},
|
||||
tags: %Schema{type: :array}
|
||||
}
|
||||
}
|
||||
],
|
||||
example: %{
|
||||
"category" => "Fun",
|
||||
"shortcode" => "aaaa",
|
||||
"url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png",
|
||||
"visible_in_picker" => true,
|
||||
"tags" => ["Gif", "Fun"]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -6,8 +6,6 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Helpers
|
||||
alias Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest
|
||||
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
@ -22,7 +20,13 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
|
||||
security: [%{"oAuth" => ["follow", "read:blocks"]}],
|
||||
operationId: "DomainBlockController.index",
|
||||
responses: %{
|
||||
200 => Operation.response("Domain blocks", "application/json", DomainBlocksResponse)
|
||||
200 =>
|
||||
Operation.response("Domain blocks", "application/json", %Schema{
|
||||
description: "Response schema for domain blocks",
|
||||
type: :array,
|
||||
items: %Schema{type: :string},
|
||||
example: ["google.com", "facebook.com"]
|
||||
})
|
||||
}
|
||||
}
|
||||
end
|
||||
@ -40,7 +44,7 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
|
||||
- prevent following new users from it (but does not remove existing follows)
|
||||
""",
|
||||
operationId: "DomainBlockController.create",
|
||||
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
|
||||
requestBody: domain_block_request(),
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||
@ -54,11 +58,28 @@ defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
|
||||
summary: "Unblock a domain",
|
||||
description: "Remove a domain block, if it exists in the user's array of blocked domains.",
|
||||
operationId: "DomainBlockController.delete",
|
||||
requestBody: Helpers.request_body("Parameters", DomainBlockRequest, required: true),
|
||||
requestBody: domain_block_request(),
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp domain_block_request do
|
||||
Helpers.request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
domain: %Schema{type: :string}
|
||||
},
|
||||
required: [:domain]
|
||||
},
|
||||
required: true,
|
||||
example: %{
|
||||
"domain" => "facebook.com"
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
231
lib/pleroma/web/api_spec/render_error.ex
Normal file
231
lib/pleroma/web/api_spec/render_error.ex
Normal file
@ -0,0 +1,231 @@
|
||||
# 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.RenderError do
|
||||
@behaviour Plug
|
||||
|
||||
import Plug.Conn, only: [put_status: 2]
|
||||
import Phoenix.Controller, only: [json: 2]
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
@impl Plug
|
||||
def init(opts), do: opts
|
||||
|
||||
@impl Plug
|
||||
|
||||
def call(conn, errors) do
|
||||
errors =
|
||||
Enum.map(errors, fn
|
||||
%{name: nil} = err ->
|
||||
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
||||
|
||||
err ->
|
||||
err
|
||||
end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{
|
||||
error: errors |> Enum.map(&message/1) |> Enum.join(" "),
|
||||
errors: errors |> Enum.map(&render_error/1)
|
||||
})
|
||||
end
|
||||
|
||||
defp render_error(error) do
|
||||
pointer = OpenApiSpex.path_to_string(error)
|
||||
|
||||
%{
|
||||
title: "Invalid value",
|
||||
source: %{
|
||||
pointer: pointer
|
||||
},
|
||||
message: OpenApiSpex.Cast.Error.message(error)
|
||||
}
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_schema_type, type: type, name: name}) do
|
||||
gettext("%{name} - Invalid schema.type. Got: %{type}.",
|
||||
name: name,
|
||||
type: inspect(type)
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :null_value, name: name} = error) do
|
||||
case error.type do
|
||||
nil ->
|
||||
gettext("%{name} - null value.", name: name)
|
||||
|
||||
type ->
|
||||
gettext("%{name} - null value where %{type} expected.",
|
||||
name: name,
|
||||
type: type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
|
||||
gettext(
|
||||
"Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
|
||||
invalid_schema: invalid_schema
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||
gettext("Failed to cast value using any of: %{failed_schemas}.",
|
||||
failed_schemas: failed_schemas
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||
gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_length, length: length, name: name}) do
|
||||
gettext("%{name} - String length is smaller than minLength: %{length}.",
|
||||
name: name,
|
||||
length: length
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_length, length: length, name: name}) do
|
||||
gettext("%{name} - String length is larger than maxLength: %{length}.",
|
||||
name: name,
|
||||
length: length
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unique_items, name: name}) do
|
||||
gettext("%{name} - Array items must be unique.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_items, length: min, value: array, name: name}) do
|
||||
gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
|
||||
name: name,
|
||||
length: length(array),
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_items, length: max, value: array, name: name}) do
|
||||
gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
|
||||
name: name,
|
||||
length: length(array),
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
|
||||
gettext("%{name} - %{count} is not a multiple of %{multiple}.",
|
||||
name: name,
|
||||
count: count,
|
||||
multiple: multiple
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :exclusive_max, length: max, value: value, name: name})
|
||||
when value >= max do
|
||||
gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
|
||||
name: name,
|
||||
value: value,
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :maximum, length: max, value: value, name: name})
|
||||
when value > max do
|
||||
gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
|
||||
name: name,
|
||||
value: value,
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
|
||||
when value <= min do
|
||||
gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
|
||||
name: name,
|
||||
value: value,
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :minimum, length: min, value: value, name: name})
|
||||
when value < min do
|
||||
gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
|
||||
name: name,
|
||||
value: value,
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do
|
||||
gettext("%{name} - Invalid %{type}. Got: %{value}.",
|
||||
name: name,
|
||||
value: OpenApiSpex.TermType.type(value),
|
||||
type: type
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_format, format: format, name: name}) do
|
||||
gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_enum, name: name}) do
|
||||
gettext("%{name} - Invalid value for enum.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
|
||||
gettext("Failed to cast to any schema in %{polymorphic_type}",
|
||||
polymorphic_type: polymorphic_type
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unexpected_field, name: name}) do
|
||||
gettext("Unexpected field: %{name}.", name: safe_string(name))
|
||||
end
|
||||
|
||||
defp message(%{reason: :no_value_for_discriminator, name: field}) do
|
||||
gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_discriminator_value, name: field}) do
|
||||
gettext("No value provided for required discriminator `%{field}`.", name: field)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unknown_schema, name: name}) do
|
||||
gettext("Unknown schema: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :missing_field, name: name}) do
|
||||
gettext("Missing field: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :missing_header, name: name}) do
|
||||
gettext("Missing header: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_header, name: name}) do
|
||||
gettext("Invalid value for header: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_properties, meta: meta}) do
|
||||
gettext(
|
||||
"Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
|
||||
property_count: meta.property_count,
|
||||
max_properties: meta.max_properties
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_properties, meta: meta}) do
|
||||
gettext(
|
||||
"Object property count %{property_count} is less than minProperties: %{min_properties}",
|
||||
property_count: meta.property_count,
|
||||
min_properties: meta.min_properties
|
||||
)
|
||||
end
|
||||
|
||||
defp safe_string(string) do
|
||||
to_string(string) |> String.slice(0..39)
|
||||
end
|
||||
end
|
167
lib/pleroma/web/api_spec/schemas/account.ex
Normal file
167
lib/pleroma/web/api_spec/schemas/account.ex
Normal file
@ -0,0 +1,167 @@
|
||||
# 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.Schemas.Account do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountField
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Account",
|
||||
description: "Response schema for an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
acct: %Schema{type: :string},
|
||||
avatar_static: %Schema{type: :string, format: :uri},
|
||||
avatar: %Schema{type: :string, format: :uri},
|
||||
bot: %Schema{type: :boolean},
|
||||
created_at: %Schema{type: :string, format: "date-time"},
|
||||
display_name: %Schema{type: :string},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
fields: %Schema{type: :array, items: AccountField},
|
||||
follow_requests_count: %Schema{type: :integer},
|
||||
followers_count: %Schema{type: :integer},
|
||||
following_count: %Schema{type: :integer},
|
||||
header_static: %Schema{type: :string, format: :uri},
|
||||
header: %Schema{type: :string, format: :uri},
|
||||
id: FlakeID,
|
||||
locked: %Schema{type: :boolean},
|
||||
note: %Schema{type: :string, format: :html},
|
||||
statuses_count: %Schema{type: :integer},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
username: %Schema{type: :string},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
allow_following_move: %Schema{type: :boolean},
|
||||
background_image: %Schema{type: :string, nullable: true},
|
||||
chat_token: %Schema{type: :string},
|
||||
confirmation_pending: %Schema{type: :boolean},
|
||||
hide_favorites: %Schema{type: :boolean},
|
||||
hide_followers_count: %Schema{type: :boolean},
|
||||
hide_followers: %Schema{type: :boolean},
|
||||
hide_follows_count: %Schema{type: :boolean},
|
||||
hide_follows: %Schema{type: :boolean},
|
||||
is_admin: %Schema{type: :boolean},
|
||||
is_moderator: %Schema{type: :boolean},
|
||||
skip_thread_containment: %Schema{type: :boolean},
|
||||
tags: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
unread_conversation_count: %Schema{type: :integer},
|
||||
notification_settings: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
followers: %Schema{type: :boolean},
|
||||
follows: %Schema{type: :boolean},
|
||||
non_followers: %Schema{type: :boolean},
|
||||
non_follows: %Schema{type: :boolean},
|
||||
privacy_option: %Schema{type: :boolean}
|
||||
}
|
||||
},
|
||||
relationship: AccountRelationship,
|
||||
settings_store: %Schema{
|
||||
type: :object
|
||||
}
|
||||
}
|
||||
},
|
||||
source: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
fields: %Schema{type: :array, items: AccountField},
|
||||
note: %Schema{type: :string},
|
||||
privacy: VisibilityScope,
|
||||
sensitive: %Schema{type: :boolean},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor_type: ActorType,
|
||||
discoverable: %Schema{type: :boolean},
|
||||
no_rich_text: %Schema{type: :boolean},
|
||||
show_role: %Schema{type: :boolean}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"acct" => "foobar",
|
||||
"avatar" => "https://mypleroma.com/images/avi.png",
|
||||
"avatar_static" => "https://mypleroma.com/images/avi.png",
|
||||
"bot" => false,
|
||||
"created_at" => "2020-03-24T13:05:58.000Z",
|
||||
"display_name" => "foobar",
|
||||
"emojis" => [],
|
||||
"fields" => [],
|
||||
"follow_requests_count" => 0,
|
||||
"followers_count" => 0,
|
||||
"following_count" => 1,
|
||||
"header" => "https://mypleroma.com/images/banner.png",
|
||||
"header_static" => "https://mypleroma.com/images/banner.png",
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"locked" => false,
|
||||
"note" => "cofe",
|
||||
"pleroma" => %{
|
||||
"allow_following_move" => true,
|
||||
"background_image" => nil,
|
||||
"confirmation_pending" => true,
|
||||
"hide_favorites" => true,
|
||||
"hide_followers" => false,
|
||||
"hide_followers_count" => false,
|
||||
"hide_follows" => false,
|
||||
"hide_follows_count" => false,
|
||||
"is_admin" => false,
|
||||
"is_moderator" => false,
|
||||
"skip_thread_containment" => false,
|
||||
"chat_token" =>
|
||||
"SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
|
||||
"unread_conversation_count" => 0,
|
||||
"tags" => [],
|
||||
"notification_settings" => %{
|
||||
"followers" => true,
|
||||
"follows" => true,
|
||||
"non_followers" => true,
|
||||
"non_follows" => true,
|
||||
"privacy_option" => false
|
||||
},
|
||||
"relationship" => %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => false,
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
},
|
||||
"settings_store" => %{
|
||||
"pleroma-fe" => %{}
|
||||
}
|
||||
},
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
"note" => "foobar",
|
||||
"pleroma" => %{
|
||||
"actor_type" => "Person",
|
||||
"discoverable" => false,
|
||||
"no_rich_text" => false,
|
||||
"show_role" => true
|
||||
},
|
||||
"privacy" => "public",
|
||||
"sensitive" => false
|
||||
},
|
||||
"statuses_count" => 0,
|
||||
"url" => "https://mypleroma.com/users/foobar",
|
||||
"username" => "foobar"
|
||||
}
|
||||
})
|
||||
end
|
26
lib/pleroma/web/api_spec/schemas/account_field.ex
Normal file
26
lib/pleroma/web/api_spec/schemas/account_field.ex
Normal file
@ -0,0 +1,26 @@
|
||||
# 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.Schemas.AccountField do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AccountField",
|
||||
description: "Response schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
value: %Schema{type: :string, format: :html},
|
||||
verified_at: %Schema{type: :string, format: :"date-time", nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"name" => "Website",
|
||||
"value" =>
|
||||
"<a href=\"https://pleroma.com\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">pleroma.com</span><span class=\"invisible\"></span></a>",
|
||||
"verified_at" => "2019-08-29T04:14:55.571+00:00"
|
||||
}
|
||||
})
|
||||
end
|
44
lib/pleroma/web/api_spec/schemas/account_relationship.ex
Normal file
44
lib/pleroma/web/api_spec/schemas/account_relationship.ex
Normal file
@ -0,0 +1,44 @@
|
||||
# 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.Schemas.AccountRelationship do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AccountRelationship",
|
||||
description: "Response schema for relationship",
|
||||
type: :object,
|
||||
properties: %{
|
||||
blocked_by: %Schema{type: :boolean},
|
||||
blocking: %Schema{type: :boolean},
|
||||
domain_blocking: %Schema{type: :boolean},
|
||||
endorsed: %Schema{type: :boolean},
|
||||
followed_by: %Schema{type: :boolean},
|
||||
following: %Schema{type: :boolean},
|
||||
id: FlakeID,
|
||||
muting: %Schema{type: :boolean},
|
||||
muting_notifications: %Schema{type: :boolean},
|
||||
requested: %Schema{type: :boolean},
|
||||
showing_reblogs: %Schema{type: :boolean},
|
||||
subscribing: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => false,
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
}
|
||||
})
|
||||
end
|
13
lib/pleroma/web/api_spec/schemas/actor_type.ex
Normal file
13
lib/pleroma/web/api_spec/schemas/actor_type.ex
Normal file
@ -0,0 +1,13 @@
|
||||
# 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.Schemas.ActorType do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ActorType",
|
||||
type: :string,
|
||||
enum: ["Application", "Group", "Organization", "Person", "Service"]
|
||||
})
|
||||
end
|
@ -2,19 +2,18 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.DomainBlockRequest do
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "DomainBlockRequest",
|
||||
title: "ApiError",
|
||||
description: "Response schema for API error",
|
||||
type: :object,
|
||||
properties: %{
|
||||
domain: %Schema{type: :string}
|
||||
},
|
||||
required: [:domain],
|
||||
properties: %{error: %Schema{type: :string}},
|
||||
example: %{
|
||||
"domain" => "facebook.com"
|
||||
"error" => "Something went wrong"
|
||||
}
|
||||
})
|
||||
end
|
@ -1,33 +0,0 @@
|
||||
# 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.Schemas.AppCreateRequest do
|
||||
alias OpenApiSpex.Schema
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AppCreateRequest",
|
||||
description: "POST body for creating an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
client_name: %Schema{type: :string, description: "A name for your application."},
|
||||
redirect_uris: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
|
||||
},
|
||||
scopes: %Schema{
|
||||
type: :string,
|
||||
description: "Space separated list of scopes. If none is provided, defaults to `read`."
|
||||
},
|
||||
website: %Schema{type: :string, description: "A URL to the homepage of your app"}
|
||||
},
|
||||
required: [:client_name, :redirect_uris],
|
||||
example: %{
|
||||
"client_name" => "My App",
|
||||
"redirect_uris" => "https://myapp.com/auth/callback",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
})
|
||||
end
|
@ -1,33 +0,0 @@
|
||||
# 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.Schemas.AppCreateResponse do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AppCreateResponse",
|
||||
description: "Response schema for an app",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
name: %Schema{type: :string},
|
||||
client_id: %Schema{type: :string},
|
||||
client_secret: %Schema{type: :string},
|
||||
redirect_uri: %Schema{type: :string},
|
||||
vapid_key: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"name" => "My App",
|
||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||
"vapid_key" =>
|
||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||
"website" => "https://myapp.com/"
|
||||
}
|
||||
})
|
||||
end
|
36
lib/pleroma/web/api_spec/schemas/boolean_like.ex
Normal file
36
lib/pleroma/web/api_spec/schemas/boolean_like.ex
Normal file
@ -0,0 +1,36 @@
|
||||
# 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.Schemas.BooleanLike do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "BooleanLike",
|
||||
description: """
|
||||
The following values will be treated as `false`:
|
||||
- false
|
||||
- 0
|
||||
- "0",
|
||||
- "f",
|
||||
- "F",
|
||||
- "false",
|
||||
- "FALSE",
|
||||
- "off",
|
||||
- "OFF"
|
||||
|
||||
All other non-null values will be treated as `true`
|
||||
""",
|
||||
anyOf: [
|
||||
%Schema{type: :boolean},
|
||||
%Schema{type: :string},
|
||||
%Schema{type: :integer}
|
||||
]
|
||||
})
|
||||
|
||||
def after_cast(value, _schmea) do
|
||||
{:ok, Pleroma.Web.ControllerHelper.truthy_param?(value)}
|
||||
end
|
||||
end
|
@ -1,30 +0,0 @@
|
||||
# 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.Schemas.CustomEmoji do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "CustomEmoji",
|
||||
description: "Response schema for an CustomEmoji",
|
||||
type: :object,
|
||||
properties: %{
|
||||
shortcode: %Schema{type: :string},
|
||||
url: %Schema{type: :string},
|
||||
static_url: %Schema{type: :string},
|
||||
visible_in_picker: %Schema{type: :boolean},
|
||||
category: %Schema{type: :string},
|
||||
tags: %Schema{type: :array}
|
||||
},
|
||||
example: %{
|
||||
"shortcode" => "aaaa",
|
||||
"url" => "https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
})
|
||||
end
|
@ -1,42 +0,0 @@
|
||||
# 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.Schemas.CustomEmojisResponse do
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "CustomEmojisResponse",
|
||||
description: "Response schema for custom emojis",
|
||||
type: :array,
|
||||
items: CustomEmoji,
|
||||
example: [
|
||||
%{
|
||||
"category" => "Fun",
|
||||
"shortcode" => "blank",
|
||||
"static_url" => "https://lain.com/emoji/blank.png",
|
||||
"tags" => ["Fun"],
|
||||
"url" => "https://lain.com/emoji/blank.png",
|
||||
"visible_in_picker" => true
|
||||
},
|
||||
%{
|
||||
"category" => "Gif,Fun",
|
||||
"shortcode" => "firefox",
|
||||
"static_url" => "https://lain.com/emoji/Firefox.gif",
|
||||
"tags" => ["Gif", "Fun"],
|
||||
"url" => "https://lain.com/emoji/Firefox.gif",
|
||||
"visible_in_picker" => true
|
||||
},
|
||||
%{
|
||||
"category" => "pack:mixed",
|
||||
"shortcode" => "sadcat",
|
||||
"static_url" => "https://lain.com/emoji/mixed/sadcat.png",
|
||||
"tags" => ["pack:mixed"],
|
||||
"url" => "https://lain.com/emoji/mixed/sadcat.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
]
|
||||
})
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
# 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.Schemas.DomainBlocksResponse do
|
||||
require OpenApiSpex
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "DomainBlocksResponse",
|
||||
description: "Response schema for domain blocks",
|
||||
type: :array,
|
||||
items: %Schema{type: :string},
|
||||
example: ["google.com", "facebook.com"]
|
||||
})
|
||||
end
|
29
lib/pleroma/web/api_spec/schemas/emoji.ex
Normal file
29
lib/pleroma/web/api_spec/schemas/emoji.ex
Normal file
@ -0,0 +1,29 @@
|
||||
# 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.Schemas.Emoji do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Emoji",
|
||||
description: "Response schema for an emoji",
|
||||
type: :object,
|
||||
properties: %{
|
||||
shortcode: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
static_url: %Schema{type: :string, format: :uri},
|
||||
visible_in_picker: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"shortcode" => "fatyoshi",
|
||||
"url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
})
|
||||
end
|
14
lib/pleroma/web/api_spec/schemas/flake_id.ex
Normal file
14
lib/pleroma/web/api_spec/schemas/flake_id.ex
Normal file
@ -0,0 +1,14 @@
|
||||
# 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.Schemas.FlakeID do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "FlakeID",
|
||||
description:
|
||||
"Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings",
|
||||
type: :string
|
||||
})
|
||||
end
|
36
lib/pleroma/web/api_spec/schemas/poll.ex
Normal file
36
lib/pleroma/web/api_spec/schemas/poll.ex
Normal file
@ -0,0 +1,36 @@
|
||||
# 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.Schemas.Poll do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Poll",
|
||||
description: "Response schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: FlakeID,
|
||||
expires_at: %Schema{type: :string, format: "date-time"},
|
||||
expired: %Schema{type: :boolean},
|
||||
multiple: %Schema{type: :boolean},
|
||||
votes_count: %Schema{type: :integer},
|
||||
voted: %Schema{type: :boolean},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
options: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string},
|
||||
votes_count: %Schema{type: :integer}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
226
lib/pleroma/web/api_spec/schemas/status.ex
Normal file
226
lib/pleroma/web/api_spec/schemas/status.ex
Normal file
@ -0,0 +1,226 @@
|
||||
# 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.Schemas.Status do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Status",
|
||||
description: "Response schema for a status",
|
||||
type: :object,
|
||||
properties: %{
|
||||
account: Account,
|
||||
application: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true, format: :uri}
|
||||
}
|
||||
},
|
||||
bookmarked: %Schema{type: :boolean},
|
||||
card: %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}
|
||||
}
|
||||
},
|
||||
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},
|
||||
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},
|
||||
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}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mentions: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
acct: %Schema{type: :string},
|
||||
username: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri}
|
||||
}
|
||||
}
|
||||
},
|
||||
muted: %Schema{type: :boolean},
|
||||
pinned: %Schema{type: :boolean},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
content: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
|
||||
conversation_id: %Schema{type: :integer},
|
||||
direct_conversation_id: %Schema{type: :string, nullable: true},
|
||||
emoji_reactions: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
count: %Schema{type: :integer},
|
||||
me: %Schema{type: :boolean}
|
||||
}
|
||||
}
|
||||
},
|
||||
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}
|
||||
}
|
||||
},
|
||||
poll: %Schema{type: Poll, nullable: true},
|
||||
reblog: %Schema{
|
||||
allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}],
|
||||
nullable: true
|
||||
},
|
||||
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: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri}
|
||||
}
|
||||
}
|
||||
},
|
||||
uri: %Schema{type: :string, format: :uri},
|
||||
url: %Schema{type: :string, nullable: true, format: :uri},
|
||||
visibility: VisibilityScope
|
||||
},
|
||||
example: %{
|
||||
"account" => %{
|
||||
"acct" => "nick6",
|
||||
"avatar" => "http://localhost:4001/images/avi.png",
|
||||
"avatar_static" => "http://localhost:4001/images/avi.png",
|
||||
"bot" => false,
|
||||
"created_at" => "2020-04-07T19:48:51.000Z",
|
||||
"display_name" => "Test テスト User 6",
|
||||
"emojis" => [],
|
||||
"fields" => [],
|
||||
"followers_count" => 1,
|
||||
"following_count" => 0,
|
||||
"header" => "http://localhost:4001/images/banner.png",
|
||||
"header_static" => "http://localhost:4001/images/banner.png",
|
||||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"locked" => false,
|
||||
"note" => "Tester Number 6",
|
||||
"pleroma" => %{
|
||||
"background_image" => nil,
|
||||
"confirmation_pending" => false,
|
||||
"hide_favorites" => true,
|
||||
"hide_followers" => false,
|
||||
"hide_followers_count" => false,
|
||||
"hide_follows" => false,
|
||||
"hide_follows_count" => false,
|
||||
"is_admin" => false,
|
||||
"is_moderator" => false,
|
||||
"relationship" => %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => true,
|
||||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
},
|
||||
"skip_thread_containment" => false,
|
||||
"tags" => []
|
||||
},
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
"note" => "Tester Number 6",
|
||||
"pleroma" => %{"actor_type" => "Person", "discoverable" => false},
|
||||
"sensitive" => false
|
||||
},
|
||||
"statuses_count" => 1,
|
||||
"url" => "http://localhost:4001/users/nick6",
|
||||
"username" => "nick6"
|
||||
},
|
||||
"application" => %{"name" => "Web", "website" => nil},
|
||||
"bookmarked" => false,
|
||||
"card" => nil,
|
||||
"content" => "foobar",
|
||||
"created_at" => "2020-04-07T19:48:51.000Z",
|
||||
"emojis" => [],
|
||||
"favourited" => false,
|
||||
"favourites_count" => 0,
|
||||
"id" => "9toJCu5YZW7O7gfvH6",
|
||||
"in_reply_to_account_id" => nil,
|
||||
"in_reply_to_id" => nil,
|
||||
"language" => nil,
|
||||
"media_attachments" => [],
|
||||
"mentions" => [],
|
||||
"muted" => false,
|
||||
"pinned" => false,
|
||||
"pleroma" => %{
|
||||
"content" => %{"text/plain" => "foobar"},
|
||||
"conversation_id" => 345_972,
|
||||
"direct_conversation_id" => nil,
|
||||
"emoji_reactions" => [],
|
||||
"expires_at" => nil,
|
||||
"in_reply_to_account_acct" => nil,
|
||||
"local" => true,
|
||||
"spoiler_text" => %{"text/plain" => ""},
|
||||
"thread_muted" => false
|
||||
},
|
||||
"poll" => nil,
|
||||
"reblog" => nil,
|
||||
"reblogged" => false,
|
||||
"reblogs_count" => 0,
|
||||
"replies_count" => 0,
|
||||
"sensitive" => false,
|
||||
"spoiler_text" => "",
|
||||
"tags" => [],
|
||||
"uri" => "http://localhost:4001/objects/0f5dad44-0e9e-4610-b377-a2631e499190",
|
||||
"url" => "http://localhost:4001/notice/9toJCu5YZW7O7gfvH6",
|
||||
"visibility" => "private"
|
||||
}
|
||||
})
|
||||
end
|
14
lib/pleroma/web/api_spec/schemas/visibility_scope.ex
Normal file
14
lib/pleroma/web/api_spec/schemas/visibility_scope.ex
Normal file
@ -0,0 +1,14 @@
|
||||
# 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.Schemas.VisibilityScope do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "VisibilityScope",
|
||||
description: "Status visibility",
|
||||
type: :string,
|
||||
enum: ["public", "unlisted", "private", "direct"]
|
||||
})
|
||||
end
|
@ -84,14 +84,18 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||
%__MODULE__{draft | attachments: attachments}
|
||||
end
|
||||
|
||||
defp in_reply_to(draft) do
|
||||
case Map.get(draft.params, "in_reply_to_status_id") do
|
||||
"" -> draft
|
||||
nil -> draft
|
||||
id -> %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)}
|
||||
end
|
||||
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
|
||||
%__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
|
||||
%__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"])
|
||||
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
|
||||
|
@ -86,8 +86,9 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
end
|
||||
end
|
||||
|
||||
def repeat(id_or_ap_id, user, params \\ %{}) do
|
||||
with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)},
|
||||
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
|
||||
@ -102,8 +103,9 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
end
|
||||
end
|
||||
|
||||
def unrepeat(id_or_ap_id, user) do
|
||||
with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do
|
||||
def unrepeat(id, user) do
|
||||
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
|
||||
{:find_activity, Activity.get_by_id(id)} do
|
||||
object = Object.normalize(activity)
|
||||
ActivityPub.unannounce(user, object)
|
||||
else
|
||||
@ -160,8 +162,9 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
end
|
||||
end
|
||||
|
||||
def unfavorite(id_or_ap_id, user) do
|
||||
with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)} do
|
||||
def unfavorite(id, user) do
|
||||
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
|
||||
{:find_activity, Activity.get_by_id(id)} do
|
||||
object = Object.normalize(activity)
|
||||
ActivityPub.unlike(user, object)
|
||||
else
|
||||
@ -332,12 +335,12 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
|
||||
defp maybe_create_activity_expiration(result, _), do: result
|
||||
|
||||
def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
||||
def pin(id, %{ap_id: user_ap_id} = user) do
|
||||
with %Activity{
|
||||
actor: ^user_ap_id,
|
||||
data: %{"type" => "Create"},
|
||||
object: %Object{data: %{"type" => object_type}}
|
||||
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- object_type in ["Note", "Article", "Question"],
|
||||
true <- Visibility.is_public?(activity),
|
||||
{:ok, _user} <- User.add_pinnned_activity(user, activity) do
|
||||
@ -348,8 +351,8 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
end
|
||||
end
|
||||
|
||||
def unpin(id_or_ap_id, user) do
|
||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||
def unpin(id, user) do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||
{:ok, _user} <- User.remove_pinnned_activity(user, activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
@ -22,24 +22,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
# This is a hack for twidere.
|
||||
def get_by_id_or_ap_id(id) do
|
||||
activity =
|
||||
with true <- FlakeId.flake_id?(id),
|
||||
%Activity{} = activity <- Activity.get_by_id_with_object(id) do
|
||||
activity
|
||||
else
|
||||
_ -> Activity.get_create_by_object_ap_id_with_object(id)
|
||||
end
|
||||
|
||||
activity &&
|
||||
if activity.data["type"] == "Create" do
|
||||
activity
|
||||
else
|
||||
Activity.get_create_by_object_ap_id_with_object(activity.data["object"])
|
||||
end
|
||||
end
|
||||
|
||||
def attachments_from_ids(%{"media_ids" => ids, "descriptions" => desc} = _) do
|
||||
attachments_from_ids_descs(ids, desc)
|
||||
end
|
||||
|
@ -82,8 +82,9 @@ defmodule Pleroma.Web.ControllerHelper do
|
||||
end
|
||||
end
|
||||
|
||||
def assign_account_by_id(%{params: %{"id" => id}} = conn, _) do
|
||||
case Pleroma.User.get_cached_by_id(id) do
|
||||
def assign_account_by_id(conn, _) do
|
||||
# TODO: use `conn.params[:id]` only after moving to OpenAPI
|
||||
case Pleroma.User.get_cached_by_id(conn.params[:id] || conn.params["id"]) do
|
||||
%Pleroma.User{} = account -> assign(conn, :account, account)
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
|
@ -27,6 +27,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||
|
||||
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||
|
||||
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:show, :statuses])
|
||||
@ -88,26 +90,26 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation
|
||||
|
||||
@doc "POST /api/v1/accounts"
|
||||
def create(
|
||||
%{assigns: %{app: app}} = conn,
|
||||
%{"username" => nickname, "password" => _, "agreement" => true} = params
|
||||
) do
|
||||
def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do
|
||||
params =
|
||||
params
|
||||
|> Map.take([
|
||||
"email",
|
||||
"captcha_solution",
|
||||
"captcha_token",
|
||||
"captcha_answer_data",
|
||||
"token",
|
||||
"password"
|
||||
:email,
|
||||
:bio,
|
||||
:captcha_solution,
|
||||
:captcha_token,
|
||||
:captcha_answer_data,
|
||||
:token,
|
||||
:password,
|
||||
:fullname
|
||||
])
|
||||
|> Map.put("nickname", nickname)
|
||||
|> Map.put("fullname", params["fullname"] || nickname)
|
||||
|> Map.put("bio", params["bio"] || "")
|
||||
|> Map.put("confirm", params["password"])
|
||||
|> Map.put("trusted_app", app.trusted)
|
||||
|> Map.put(:nickname, params.username)
|
||||
|> Map.put(:fullname, Map.get(params, :fullname, params.username))
|
||||
|> Map.put(:confirm, params.password)
|
||||
|> Map.put(:trusted_app, app.trusted)
|
||||
|
||||
with :ok <- validate_email_param(params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
@ -131,7 +133,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
render_error(conn, :forbidden, "Invalid credentials")
|
||||
end
|
||||
|
||||
defp validate_email_param(%{"email" => _}), do: :ok
|
||||
defp validate_email_param(%{:email => email}) when not is_nil(email), do: :ok
|
||||
|
||||
defp validate_email_param(_) do
|
||||
case Pleroma.Config.get([:instance, :account_activation_required]) do
|
||||
@ -153,7 +155,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
end
|
||||
|
||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||
def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||
def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do
|
||||
user = original_user
|
||||
|
||||
params =
|
||||
params
|
||||
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
user_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
@ -169,22 +178,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
:discoverable
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||
add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)})
|
||||
end)
|
||||
|> add_if_present(params, "display_name", :name)
|
||||
|> add_if_present(params, "note", :bio)
|
||||
|> add_if_present(params, "avatar", :avatar)
|
||||
|> add_if_present(params, "header", :banner)
|
||||
|> add_if_present(params, "pleroma_background_image", :background)
|
||||
|> add_if_present(params, :display_name, :name)
|
||||
|> add_if_present(params, :note, :bio)
|
||||
|> add_if_present(params, :avatar, :avatar)
|
||||
|> add_if_present(params, :header, :banner)
|
||||
|> add_if_present(params, :pleroma_background_image, :background)
|
||||
|> add_if_present(
|
||||
params,
|
||||
"fields_attributes",
|
||||
:fields_attributes,
|
||||
:raw_fields,
|
||||
&{:ok, normalize_fields_attributes(&1)}
|
||||
)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "actor_type", :actor_type)
|
||||
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
||||
|> add_if_present(params, :default_scope, :default_scope)
|
||||
|> add_if_present(params, :actor_type, :actor_type)
|
||||
|
||||
changeset = User.update_changeset(user, user_params)
|
||||
|
||||
@ -197,7 +206,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
||||
with true <- Map.has_key?(params, params_field),
|
||||
{:ok, new_value} <- value_function.(params[params_field]) do
|
||||
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
||||
Map.put(map, map_field, new_value)
|
||||
else
|
||||
_ -> map
|
||||
@ -208,12 +217,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
if Enum.all?(fields, &is_tuple/1) do
|
||||
Enum.map(fields, fn {_, v} -> v end)
|
||||
else
|
||||
fields
|
||||
Enum.map(fields, fn
|
||||
%{} = field -> %{"name" => field.name, "value" => field.value}
|
||||
field -> field
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/accounts/relationships"
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
targets = User.get_all_by_ids(List.wrap(id))
|
||||
|
||||
render(conn, "relationships.json", user: user, targets: targets)
|
||||
@ -223,7 +235,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
||||
|
||||
@doc "GET /api/v1/accounts/:id"
|
||||
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||
def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
||||
true <- User.visible_for?(user, for_user) do
|
||||
render(conn, "show.json", user: user, for: for_user)
|
||||
@ -234,12 +246,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "GET /api/v1/accounts/:id/statuses"
|
||||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user),
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
|
||||
true <- User.visible_for?(user, reading_user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("tag", params["tagged"])
|
||||
|> Map.delete("godmode")
|
||||
|> Map.delete(:tagged)
|
||||
|> Enum.filter(&(not is_nil(&1)))
|
||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Map.put("tag", params[:tagged])
|
||||
|
||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||
|
||||
@ -259,6 +273,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "GET /api/v1/accounts/:id/followers"
|
||||
def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params)
|
||||
@ -273,6 +292,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "GET /api/v1/accounts/:id/following"
|
||||
def following(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params)
|
||||
@ -299,8 +323,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
{:error, "Can not follow yourself"}
|
||||
end
|
||||
|
||||
def follow(%{assigns: %{user: follower, account: followed}} = conn, _params) do
|
||||
with {:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do
|
||||
def follow(%{assigns: %{user: follower, account: followed}} = conn, params) do
|
||||
with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do
|
||||
render(conn, "relationship.json", user: follower, target: followed)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
@ -319,10 +343,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do
|
||||
notifications? = params |> Map.get("notifications", true) |> truthy_param?()
|
||||
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, notifications?) do
|
||||
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
@ -359,7 +381,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
end
|
||||
|
||||
@doc "POST /api/v1/follows"
|
||||
def follow_by_uri(conn, %{"uri" => uri}) do
|
||||
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||
case User.get_cached_by_nickname(uri) do
|
||||
%User{} = user ->
|
||||
conn
|
||||
|
@ -127,7 +127,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
def create(
|
||||
%{assigns: %{user: user}} = conn,
|
||||
%{"status" => _, "scheduled_at" => scheduled_at} = params
|
||||
) do
|
||||
)
|
||||
when not is_nil(scheduled_at) do
|
||||
params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"])
|
||||
|
||||
with {:far_enough, true} <- {:far_enough, ScheduledActivity.far_enough?(scheduled_at)},
|
||||
|
@ -44,6 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> Map.put("user", user)
|
||||
|
||||
recipients = [user.ap_id | User.following(user)]
|
||||
@ -109,6 +110,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||
|> Map.put("local_only", local_only)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
conn
|
||||
|
@ -45,7 +45,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
end)
|
||||
end
|
||||
|
||||
defp get_user(ap_id) do
|
||||
def get_user(ap_id, fake_record_fallback \\ true) do
|
||||
cond do
|
||||
user = User.get_cached_by_ap_id(ap_id) ->
|
||||
user
|
||||
@ -53,8 +53,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
user = User.get_by_guessed_nickname(ap_id) ->
|
||||
user
|
||||
|
||||
true ->
|
||||
fake_record_fallback ->
|
||||
# TODO: refactor (fake records is never a good idea)
|
||||
User.error_user(ap_id)
|
||||
|
||||
true ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -97,7 +101,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
UserRelationship.view_relationships_option(nil, [])
|
||||
|
||||
true ->
|
||||
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))
|
||||
# Note: unresolved users are filtered out
|
||||
actors =
|
||||
(activities ++ parent_activities)
|
||||
|> Enum.map(&get_user(&1.data["actor"], false))
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
UserRelationship.view_relationships_option(reading_user, actors,
|
||||
source_mutes_only: opts[:skip_relationships]
|
||||
@ -521,11 +529,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
"""
|
||||
@spec build_tags(list(any())) :: list(map())
|
||||
def build_tags(object_tags) when is_list(object_tags) do
|
||||
object_tags = for tag when is_binary(tag) <- object_tags, do: tag
|
||||
|
||||
Enum.reduce(object_tags, [], fn tag, tags ->
|
||||
tags ++ [%{name: tag, url: "/tag/#{URI.encode(tag)}"}]
|
||||
end)
|
||||
object_tags
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|> Enum.map(&%{name: &1, url: "/tag/#{URI.encode(&1)}"})
|
||||
end
|
||||
|
||||
def build_tags(_), do: []
|
||||
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
|
||||
plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password)
|
||||
|
||||
def user_exists(conn, %{"user" => username}) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true, deactivated: false) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
@ -26,7 +26,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
|
||||
end
|
||||
|
||||
def check_password(conn, %{"user" => username, "pass" => password}) do
|
||||
with %User{password_hash: password_hash} <-
|
||||
with %User{password_hash: password_hash, deactivated: false} <-
|
||||
Repo.get_by(User, nickname: username, local: true),
|
||||
true <- Pbkdf2.checkpw(password, password_hash) do
|
||||
conn
|
||||
|
@ -17,12 +17,8 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
||||
"""
|
||||
@spec fetch_scopes(map() | struct(), list()) :: list()
|
||||
|
||||
def fetch_scopes(%Pleroma.Web.ApiSpec.Schemas.AppCreateRequest{scopes: scopes}, default) do
|
||||
parse_scopes(scopes, default)
|
||||
end
|
||||
|
||||
def fetch_scopes(params, default) do
|
||||
parse_scopes(params["scope"] || params["scopes"], default)
|
||||
parse_scopes(params["scope"] || params["scopes"] || params[:scopes], default)
|
||||
end
|
||||
|
||||
def parse_scopes(scopes, _default) when is_list(scopes) do
|
||||
|
@ -12,73 +12,57 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||
require Pleroma.Constants
|
||||
|
||||
def register_user(params, opts \\ []) do
|
||||
token = params["token"]
|
||||
trusted_app? = params["trusted_app"]
|
||||
params =
|
||||
params
|
||||
|> Map.take([
|
||||
:nickname,
|
||||
:password,
|
||||
:captcha_solution,
|
||||
:captcha_token,
|
||||
:captcha_answer_data,
|
||||
:token,
|
||||
:email,
|
||||
:trusted_app
|
||||
])
|
||||
|> Map.put(:bio, User.parse_bio(params[:bio] || ""))
|
||||
|> Map.put(:name, params.fullname)
|
||||
|> Map.put(:password_confirmation, params[:confirm])
|
||||
|
||||
params = %{
|
||||
nickname: params["nickname"],
|
||||
name: params["fullname"],
|
||||
bio: User.parse_bio(params["bio"]),
|
||||
email: params["email"],
|
||||
password: params["password"],
|
||||
password_confirmation: params["confirm"],
|
||||
captcha_solution: params["captcha_solution"],
|
||||
captcha_token: params["captcha_token"],
|
||||
captcha_answer_data: params["captcha_answer_data"]
|
||||
}
|
||||
case validate_captcha(params) do
|
||||
:ok ->
|
||||
if Pleroma.Config.get([:instance, :registrations_open]) do
|
||||
create_user(params, opts)
|
||||
else
|
||||
create_user_with_invite(params, opts)
|
||||
end
|
||||
|
||||
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
||||
# true if captcha is disabled or enabled and valid, false otherwise
|
||||
captcha_ok =
|
||||
if trusted_app? || not captcha_enabled do
|
||||
:ok
|
||||
else
|
||||
Pleroma.Captcha.validate(
|
||||
params[:captcha_token],
|
||||
params[:captcha_solution],
|
||||
params[:captcha_answer_data]
|
||||
)
|
||||
end
|
||||
{:error, error} ->
|
||||
# I have no idea how this error handling works
|
||||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
end
|
||||
end
|
||||
|
||||
# Captcha invalid
|
||||
if captcha_ok != :ok do
|
||||
{:error, error} = captcha_ok
|
||||
# I have no idea how this error handling works
|
||||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
defp validate_captcha(params) do
|
||||
if params[:trusted_app] || not Pleroma.Config.get([Pleroma.Captcha, :enabled]) do
|
||||
:ok
|
||||
else
|
||||
registration_process(
|
||||
params,
|
||||
%{
|
||||
registrations_open: Pleroma.Config.get([:instance, :registrations_open]),
|
||||
token: token
|
||||
},
|
||||
opts
|
||||
Pleroma.Captcha.validate(
|
||||
params.captcha_token,
|
||||
params.captcha_solution,
|
||||
params.captcha_answer_data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp registration_process(params, %{registrations_open: true}, opts) do
|
||||
create_user(params, opts)
|
||||
end
|
||||
|
||||
defp registration_process(params, %{token: token}, opts) do
|
||||
invite =
|
||||
unless is_nil(token) do
|
||||
Repo.get_by(UserInviteToken, %{token: token})
|
||||
end
|
||||
|
||||
valid_invite? = invite && UserInviteToken.valid_invite?(invite)
|
||||
|
||||
case invite do
|
||||
nil ->
|
||||
{:error, "Invalid token"}
|
||||
|
||||
invite when valid_invite? ->
|
||||
UserInviteToken.update_usage!(invite)
|
||||
create_user(params, opts)
|
||||
|
||||
_ ->
|
||||
{:error, "Expired token"}
|
||||
defp create_user_with_invite(params, opts) do
|
||||
with %{token: token} when is_binary(token) <- params,
|
||||
%UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}),
|
||||
true <- UserInviteToken.valid_invite?(invite) do
|
||||
UserInviteToken.update_usage!(invite)
|
||||
create_user(params, opts)
|
||||
else
|
||||
nil -> {:error, "Invalid token"}
|
||||
_ -> {:error, "Expired token"}
|
||||
end
|
||||
end
|
||||
|
||||
|
4
mix.exs
4
mix.exs
@ -189,7 +189,9 @@ defmodule Pleroma.Mixfile do
|
||||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||
{:mox, "~> 0.5", only: :test},
|
||||
{:restarter, path: "./restarter"},
|
||||
{:open_api_spex, "~> 3.6"}
|
||||
{:open_api_spex,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||
ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"}
|
||||
] ++ oauth_deps()
|
||||
end
|
||||
|
||||
|
2
mix.lock
2
mix.lock
@ -74,7 +74,7 @@
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},
|
||||
"open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "b862ebd78de0df95875cf46feb6e9607130dc2a8", [ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"]},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"},
|
||||
|
@ -2,11 +2,21 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.StateTest do
|
||||
defmodule Pleroma.StatsTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
describe "user count" do
|
||||
test "it ignores internal users" do
|
||||
_user = insert(:user, local: true)
|
||||
_internal = insert(:user, local: true, nickname: nil)
|
||||
_internal = Pleroma.Web.ActivityPub.Relay.get_actor()
|
||||
|
||||
assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data())
|
||||
end
|
||||
end
|
||||
|
||||
describe "status visibility count" do
|
||||
test "on new status" do
|
||||
user = insert(:user)
|
57
test/support/api_spec_helpers.ex
Normal file
57
test/support/api_spec_helpers.ex
Normal file
@ -0,0 +1,57 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Tests.ApiSpecHelpers do
|
||||
@moduledoc """
|
||||
OpenAPI spec test helpers
|
||||
"""
|
||||
|
||||
import ExUnit.Assertions
|
||||
|
||||
alias OpenApiSpex.Cast.Error
|
||||
alias OpenApiSpex.Reference
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def assert_schema(value, schema) do
|
||||
api_spec = Pleroma.Web.ApiSpec.spec()
|
||||
|
||||
case OpenApiSpex.cast_value(value, schema, api_spec) do
|
||||
{:ok, data} ->
|
||||
data
|
||||
|
||||
{:error, errors} ->
|
||||
errors =
|
||||
Enum.map(errors, fn error ->
|
||||
message = Error.message(error)
|
||||
path = Error.path_to_string(error)
|
||||
"#{message} at #{path}"
|
||||
end)
|
||||
|
||||
flunk(
|
||||
"Value does not conform to schema #{schema.title}: #{Enum.join(errors, "\n")}\n#{
|
||||
inspect(value)
|
||||
}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_schema(%Schema{} = schema), do: schema
|
||||
|
||||
def resolve_schema(%Reference{} = ref) do
|
||||
schemas = Pleroma.Web.ApiSpec.spec().components.schemas
|
||||
Reference.resolve_schema(ref, schemas)
|
||||
end
|
||||
|
||||
def api_operations do
|
||||
paths = Pleroma.Web.ApiSpec.spec().paths
|
||||
|
||||
Enum.flat_map(paths, fn {_, path_item} ->
|
||||
path_item
|
||||
|> Map.take([:delete, :get, :head, :options, :patch, :post, :put, :trace])
|
||||
|> Map.values()
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.uniq()
|
||||
end)
|
||||
end
|
||||
end
|
@ -51,6 +51,60 @@ defmodule Pleroma.Web.ConnCase do
|
||||
%{user: user, token: token, conn: conn}
|
||||
end
|
||||
|
||||
defp request_content_type(%{conn: conn}) do
|
||||
conn = put_req_header(conn, "content-type", "multipart/form-data")
|
||||
[conn: conn]
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(
|
||||
%{
|
||||
private: %{
|
||||
open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec}
|
||||
}
|
||||
} = conn,
|
||||
status
|
||||
) do
|
||||
content_type =
|
||||
conn
|
||||
|> Plug.Conn.get_resp_header("content-type")
|
||||
|> List.first()
|
||||
|> String.split(";")
|
||||
|> List.first()
|
||||
|
||||
status = Plug.Conn.Status.code(status)
|
||||
|
||||
unless lookup[op_id].responses[status] do
|
||||
err = "Response schema not found for #{conn.status} #{conn.method} #{conn.request_path}"
|
||||
flunk(err)
|
||||
end
|
||||
|
||||
schema = lookup[op_id].responses[status].content[content_type].schema
|
||||
json = json_response(conn, status)
|
||||
|
||||
case OpenApiSpex.cast_value(json, schema, spec) do
|
||||
{:ok, _data} ->
|
||||
json
|
||||
|
||||
{:error, errors} ->
|
||||
errors =
|
||||
Enum.map(errors, fn error ->
|
||||
message = OpenApiSpex.Cast.Error.message(error)
|
||||
path = OpenApiSpex.Cast.Error.path_to_string(error)
|
||||
"#{message} at #{path}"
|
||||
end)
|
||||
|
||||
flunk(
|
||||
"Response does not conform to schema of #{op_id} operation: #{
|
||||
Enum.join(errors, "\n")
|
||||
}\n#{inspect(json)}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(conn, _status) do
|
||||
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
|
||||
end
|
||||
|
||||
defp ensure_federating_or_authenticated(conn, url, user) do
|
||||
initial_setting = Config.get([:instance, :federating])
|
||||
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
|
||||
|
@ -994,72 +994,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "like an object" do
|
||||
test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
|
||||
Config.put([:instance, :federating], true)
|
||||
note_activity = insert(:note_activity)
|
||||
assert object_activity = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||
assert called(Federator.publish(like_activity))
|
||||
end
|
||||
|
||||
test "returns exist activity if object already liked" do
|
||||
note_activity = insert(:note_activity)
|
||||
assert object_activity = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object_activity)
|
||||
|
||||
{:ok, like_activity_exist, _object} = ActivityPub.like(user, object_activity)
|
||||
assert like_activity == like_activity_exist
|
||||
end
|
||||
|
||||
test "reverts like activity on error" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.like(user, object)
|
||||
end
|
||||
|
||||
assert Repo.aggregate(Activity, :count, :id) == 1
|
||||
assert Repo.get(Object, object.id) == object
|
||||
end
|
||||
|
||||
test "adds a like activity to the db" do
|
||||
note_activity = insert(:note_activity)
|
||||
assert object = Object.normalize(note_activity)
|
||||
|
||||
user = insert(:user)
|
||||
user_two = insert(:user)
|
||||
|
||||
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
||||
|
||||
assert like_activity.data["actor"] == user.ap_id
|
||||
assert like_activity.data["type"] == "Like"
|
||||
assert like_activity.data["object"] == object.data["id"]
|
||||
assert like_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]]
|
||||
assert like_activity.data["context"] == object.data["context"]
|
||||
assert object.data["like_count"] == 1
|
||||
assert object.data["likes"] == [user.ap_id]
|
||||
|
||||
# Just return the original activity if the user already liked it.
|
||||
{:ok, same_like_activity, object} = ActivityPub.like(user, object)
|
||||
|
||||
assert like_activity == same_like_activity
|
||||
assert object.data["likes"] == [user.ap_id]
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, _like_activity, object} = ActivityPub.like(user_two, object)
|
||||
assert object.data["like_count"] == 2
|
||||
end
|
||||
end
|
||||
|
||||
describe "unliking" do
|
||||
test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
|
||||
Config.put([:instance, :federating], true)
|
||||
@ -1071,7 +1005,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
{:ok, object} = ActivityPub.unlike(user, object)
|
||||
refute called(Federator.publish())
|
||||
|
||||
{:ok, _like_activity, object} = ActivityPub.like(user, object)
|
||||
{:ok, _like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
object = Object.get_by_id(object.id)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||
@ -1082,10 +1017,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
|
||||
test "reverts unliking on error" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
object = Object.normalize(note_activity)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
@ -1106,7 +1041,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
{:ok, object} = ActivityPub.unlike(user, object)
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
|
||||
object = Object.get_by_id(object.id)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||
@ -1973,4 +1910,497 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||
ActivityPub.move(old_user, new_user)
|
||||
end
|
||||
end
|
||||
|
||||
test "doesn't retrieve replies activities with exclude_replies" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "yeah"})
|
||||
|
||||
{:ok, _reply} =
|
||||
CommonAPI.post(user, %{"status" => "yeah", "in_reply_to_status_id" => activity.id})
|
||||
|
||||
[result] = ActivityPub.fetch_public_activities(%{"exclude_replies" => "true"})
|
||||
|
||||
assert result.id == activity.id
|
||||
|
||||
assert length(ActivityPub.fetch_public_activities()) == 2
|
||||
end
|
||||
|
||||
describe "replies filtering with public messages" do
|
||||
setup :public_messages
|
||||
|
||||
test "public timeline", %{users: %{u1: user}} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 16
|
||||
end
|
||||
|
||||
test "public timeline with reply_visibility `following`", %{
|
||||
users: %{u1: user},
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4,
|
||||
activities: activities
|
||||
} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 14
|
||||
|
||||
visible_ids =
|
||||
Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
|
||||
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
|
||||
test "public timeline with reply_visibility `self`", %{
|
||||
users: %{u1: user},
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4,
|
||||
activities: activities
|
||||
} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 10
|
||||
visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
|
||||
test "home timeline", %{
|
||||
users: %{u1: user},
|
||||
activities: activities,
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4
|
||||
} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 13
|
||||
|
||||
visible_ids =
|
||||
Map.values(u1) ++
|
||||
Map.values(u3) ++
|
||||
[
|
||||
activities[:a1],
|
||||
activities[:a2],
|
||||
activities[:a4],
|
||||
u2[:r1],
|
||||
u2[:r3],
|
||||
u4[:r1],
|
||||
u4[:r2]
|
||||
]
|
||||
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
|
||||
test "home timeline with reply_visibility `following`", %{
|
||||
users: %{u1: user},
|
||||
activities: activities,
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4
|
||||
} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 11
|
||||
|
||||
visible_ids =
|
||||
Map.values(u1) ++
|
||||
[
|
||||
activities[:a1],
|
||||
activities[:a2],
|
||||
activities[:a4],
|
||||
u2[:r1],
|
||||
u2[:r3],
|
||||
u3[:r1],
|
||||
u4[:r1],
|
||||
u4[:r2]
|
||||
]
|
||||
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
|
||||
test "home timeline with reply_visibility `self`", %{
|
||||
users: %{u1: user},
|
||||
activities: activities,
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4
|
||||
} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 9
|
||||
|
||||
visible_ids =
|
||||
Map.values(u1) ++
|
||||
[
|
||||
activities[:a1],
|
||||
activities[:a2],
|
||||
activities[:a4],
|
||||
u2[:r1],
|
||||
u3[:r1],
|
||||
u4[:r1]
|
||||
]
|
||||
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
end
|
||||
|
||||
describe "replies filtering with private messages" do
|
||||
setup :private_messages
|
||||
|
||||
test "public timeline", %{users: %{u1: user}} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert activities_ids == []
|
||||
end
|
||||
|
||||
test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert activities_ids == []
|
||||
end
|
||||
|
||||
test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
|
||||
activities_ids =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("local_only", false)
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert activities_ids == []
|
||||
end
|
||||
|
||||
test "home timeline", %{users: %{u1: user}} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 12
|
||||
end
|
||||
|
||||
test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("reply_visibility", "following")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 12
|
||||
end
|
||||
|
||||
test "home timeline with default reply_visibility `self`", %{
|
||||
users: %{u1: user},
|
||||
activities: activities,
|
||||
u1: u1,
|
||||
u2: u2,
|
||||
u3: u3,
|
||||
u4: u4
|
||||
} do
|
||||
params =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("reply_visibility", "self")
|
||||
|> Map.put("reply_filtering_user", user)
|
||||
|
||||
activities_ids =
|
||||
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
assert length(activities_ids) == 10
|
||||
|
||||
visible_ids =
|
||||
Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
|
||||
|
||||
assert Enum.all?(visible_ids, &(&1 in activities_ids))
|
||||
end
|
||||
end
|
||||
|
||||
defp public_messages(_) do
|
||||
[u1, u2, u3, u4] = insert_list(4, :user)
|
||||
{:ok, u1} = User.follow(u1, u2)
|
||||
{:ok, u2} = User.follow(u2, u1)
|
||||
{:ok, u1} = User.follow(u1, u4)
|
||||
{:ok, u4} = User.follow(u4, u1)
|
||||
|
||||
{:ok, u2} = User.follow(u2, u3)
|
||||
{:ok, u3} = User.follow(u3, u2)
|
||||
|
||||
{:ok, a1} = CommonAPI.post(u1, %{"status" => "Status"})
|
||||
|
||||
{:ok, r1_1} =
|
||||
CommonAPI.post(u2, %{
|
||||
"status" => "@#{u1.nickname} reply from u2 to u1",
|
||||
"in_reply_to_status_id" => a1.id
|
||||
})
|
||||
|
||||
{:ok, r1_2} =
|
||||
CommonAPI.post(u3, %{
|
||||
"status" => "@#{u1.nickname} reply from u3 to u1",
|
||||
"in_reply_to_status_id" => a1.id
|
||||
})
|
||||
|
||||
{:ok, r1_3} =
|
||||
CommonAPI.post(u4, %{
|
||||
"status" => "@#{u1.nickname} reply from u4 to u1",
|
||||
"in_reply_to_status_id" => a1.id
|
||||
})
|
||||
|
||||
{:ok, a2} = CommonAPI.post(u2, %{"status" => "Status"})
|
||||
|
||||
{:ok, r2_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u2.nickname} reply from u1 to u2",
|
||||
"in_reply_to_status_id" => a2.id
|
||||
})
|
||||
|
||||
{:ok, r2_2} =
|
||||
CommonAPI.post(u3, %{
|
||||
"status" => "@#{u2.nickname} reply from u3 to u2",
|
||||
"in_reply_to_status_id" => a2.id
|
||||
})
|
||||
|
||||
{:ok, r2_3} =
|
||||
CommonAPI.post(u4, %{
|
||||
"status" => "@#{u2.nickname} reply from u4 to u2",
|
||||
"in_reply_to_status_id" => a2.id
|
||||
})
|
||||
|
||||
{:ok, a3} = CommonAPI.post(u3, %{"status" => "Status"})
|
||||
|
||||
{:ok, r3_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u3.nickname} reply from u1 to u3",
|
||||
"in_reply_to_status_id" => a3.id
|
||||
})
|
||||
|
||||
{:ok, r3_2} =
|
||||
CommonAPI.post(u2, %{
|
||||
"status" => "@#{u3.nickname} reply from u2 to u3",
|
||||
"in_reply_to_status_id" => a3.id
|
||||
})
|
||||
|
||||
{:ok, r3_3} =
|
||||
CommonAPI.post(u4, %{
|
||||
"status" => "@#{u3.nickname} reply from u4 to u3",
|
||||
"in_reply_to_status_id" => a3.id
|
||||
})
|
||||
|
||||
{:ok, a4} = CommonAPI.post(u4, %{"status" => "Status"})
|
||||
|
||||
{:ok, r4_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u4.nickname} reply from u1 to u4",
|
||||
"in_reply_to_status_id" => a4.id
|
||||
})
|
||||
|
||||
{:ok, r4_2} =
|
||||
CommonAPI.post(u2, %{
|
||||
"status" => "@#{u4.nickname} reply from u2 to u4",
|
||||
"in_reply_to_status_id" => a4.id
|
||||
})
|
||||
|
||||
{:ok, r4_3} =
|
||||
CommonAPI.post(u3, %{
|
||||
"status" => "@#{u4.nickname} reply from u3 to u4",
|
||||
"in_reply_to_status_id" => a4.id
|
||||
})
|
||||
|
||||
{:ok,
|
||||
users: %{u1: u1, u2: u2, u3: u3, u4: u4},
|
||||
activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
|
||||
u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
|
||||
u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
|
||||
u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
|
||||
u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
|
||||
end
|
||||
|
||||
defp private_messages(_) do
|
||||
[u1, u2, u3, u4] = insert_list(4, :user)
|
||||
{:ok, u1} = User.follow(u1, u2)
|
||||
{:ok, u2} = User.follow(u2, u1)
|
||||
{:ok, u1} = User.follow(u1, u3)
|
||||
{:ok, u3} = User.follow(u3, u1)
|
||||
{:ok, u1} = User.follow(u1, u4)
|
||||
{:ok, u4} = User.follow(u4, u1)
|
||||
|
||||
{:ok, u2} = User.follow(u2, u3)
|
||||
{:ok, u3} = User.follow(u3, u2)
|
||||
|
||||
{:ok, a1} = CommonAPI.post(u1, %{"status" => "Status", "visibility" => "private"})
|
||||
|
||||
{:ok, r1_1} =
|
||||
CommonAPI.post(u2, %{
|
||||
"status" => "@#{u1.nickname} reply from u2 to u1",
|
||||
"in_reply_to_status_id" => a1.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, r1_2} =
|
||||
CommonAPI.post(u3, %{
|
||||
"status" => "@#{u1.nickname} reply from u3 to u1",
|
||||
"in_reply_to_status_id" => a1.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, r1_3} =
|
||||
CommonAPI.post(u4, %{
|
||||
"status" => "@#{u1.nickname} reply from u4 to u1",
|
||||
"in_reply_to_status_id" => a1.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, a2} = CommonAPI.post(u2, %{"status" => "Status", "visibility" => "private"})
|
||||
|
||||
{:ok, r2_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u2.nickname} reply from u1 to u2",
|
||||
"in_reply_to_status_id" => a2.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, r2_2} =
|
||||
CommonAPI.post(u3, %{
|
||||
"status" => "@#{u2.nickname} reply from u3 to u2",
|
||||
"in_reply_to_status_id" => a2.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, a3} = CommonAPI.post(u3, %{"status" => "Status", "visibility" => "private"})
|
||||
|
||||
{:ok, r3_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u3.nickname} reply from u1 to u3",
|
||||
"in_reply_to_status_id" => a3.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, r3_2} =
|
||||
CommonAPI.post(u2, %{
|
||||
"status" => "@#{u3.nickname} reply from u2 to u3",
|
||||
"in_reply_to_status_id" => a3.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, a4} = CommonAPI.post(u4, %{"status" => "Status", "visibility" => "private"})
|
||||
|
||||
{:ok, r4_1} =
|
||||
CommonAPI.post(u1, %{
|
||||
"status" => "@#{u4.nickname} reply from u1 to u4",
|
||||
"in_reply_to_status_id" => a4.id,
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok,
|
||||
users: %{u1: u1, u2: u2, u3: u3, u4: u4},
|
||||
activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
|
||||
u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
|
||||
u2: %{r1: r2_1.id, r2: r2_2.id},
|
||||
u3: %{r1: r3_1.id, r2: r3_2.id},
|
||||
u4: %{r1: r4_1.id}}
|
||||
end
|
||||
end
|
||||
|
@ -224,8 +224,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||
|
||||
object = Object.normalize(activity)
|
||||
{:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
|
||||
vote_object = Object.normalize(vote)
|
||||
{:ok, _activity, _object} = ActivityPub.like(user, vote_object)
|
||||
{:ok, _activity} = CommonAPI.favorite(user, activity.id)
|
||||
[fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
|
||||
assert fetched_vote.id == vote.id
|
||||
end
|
||||
@ -346,7 +345,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||
|
||||
user = insert(:user)
|
||||
refute Utils.get_existing_like(user.ap_id, object)
|
||||
{:ok, like_activity, _object} = ActivityPub.like(user, object)
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
|
||||
assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
|
||||
end
|
||||
|
@ -1,45 +0,0 @@
|
||||
# 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.AppOperationTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse
|
||||
|
||||
import OpenApiSpex.TestAssertions
|
||||
import Pleroma.Factory
|
||||
|
||||
test "AppCreateRequest example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = AppCreateRequest.schema()
|
||||
assert_schema(schema.example, "AppCreateRequest", api_spec)
|
||||
end
|
||||
|
||||
test "AppCreateResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = AppCreateResponse.schema()
|
||||
assert_schema(schema.example, "AppCreateResponse", api_spec)
|
||||
end
|
||||
|
||||
test "AppController produces a AppCreateResponse", %{conn: conn} do
|
||||
api_spec = ApiSpec.spec()
|
||||
app_attrs = build(:oauth_app)
|
||||
|
||||
json =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post(
|
||||
"/api/v1/apps",
|
||||
Jason.encode!(%{
|
||||
client_name: app_attrs.client_name,
|
||||
redirect_uris: app_attrs.redirect_uris
|
||||
})
|
||||
)
|
||||
|> json_response(200)
|
||||
|
||||
assert_schema(json, "AppCreateResponse", api_spec)
|
||||
end
|
||||
end
|
43
test/web/api_spec/schema_examples_test.exs
Normal file
43
test/web/api_spec/schema_examples_test.exs
Normal file
@ -0,0 +1,43 @@
|
||||
# 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.SchemaExamplesTest do
|
||||
use ExUnit.Case, async: true
|
||||
import Pleroma.Tests.ApiSpecHelpers
|
||||
|
||||
@content_type "application/json"
|
||||
|
||||
for operation <- api_operations() do
|
||||
describe operation.operationId <> " Request Body" do
|
||||
if operation.requestBody do
|
||||
@media_type operation.requestBody.content[@content_type]
|
||||
@schema resolve_schema(@media_type.schema)
|
||||
|
||||
if @media_type.example do
|
||||
test "request body media type example matches schema" do
|
||||
assert_schema(@media_type.example, @schema)
|
||||
end
|
||||
end
|
||||
|
||||
if @schema.example do
|
||||
test "request body schema example matches schema" do
|
||||
assert_schema(@schema.example, @schema)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for {status, response} <- operation.responses do
|
||||
describe "#{operation.operationId} - #{status} Response" do
|
||||
@schema resolve_schema(response.content[@content_type].schema)
|
||||
|
||||
if @schema.example do
|
||||
test "example matches schema" do
|
||||
assert_schema(@schema.example, @schema)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -21,6 +21,60 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||
setup do: clear_config([:instance, :limit])
|
||||
setup do: clear_config([:instance, :max_pinned_statuses])
|
||||
|
||||
test "favoriting race condition" do
|
||||
user = insert(:user)
|
||||
users_serial = insert_list(10, :user)
|
||||
users = insert_list(10, :user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "."})
|
||||
|
||||
users_serial
|
||||
|> Enum.map(fn user ->
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
end)
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["like_count"] == 10
|
||||
|
||||
users
|
||||
|> Enum.map(fn user ->
|
||||
Task.async(fn ->
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
end)
|
||||
end)
|
||||
|> Enum.map(&Task.await/1)
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["like_count"] == 20
|
||||
end
|
||||
|
||||
test "repeating race condition" do
|
||||
user = insert(:user)
|
||||
users_serial = insert_list(10, :user)
|
||||
users = insert_list(10, :user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "."})
|
||||
|
||||
users_serial
|
||||
|> Enum.map(fn user ->
|
||||
CommonAPI.repeat(activity.id, user)
|
||||
end)
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["announcement_count"] == 10
|
||||
|
||||
users
|
||||
|> Enum.map(fn user ->
|
||||
Task.async(fn ->
|
||||
CommonAPI.repeat(activity.id, user)
|
||||
end)
|
||||
end)
|
||||
|> Enum.map(&Task.await/1)
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["announcement_count"] == 20
|
||||
end
|
||||
|
||||
test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
|
||||
@ -256,6 +310,16 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||
{:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
|
||||
end
|
||||
|
||||
test "can't repeat a repeat" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||
|
||||
{:ok, %Activity{} = announce, _} = CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
refute match?({:ok, %Activity{}, _}, CommonAPI.repeat(announce.id, user))
|
||||
end
|
||||
|
||||
test "repeating a status privately" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
@ -285,8 +349,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||
{:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, %Activity{} = announce, object} = CommonAPI.repeat(activity.id, user)
|
||||
{:ok, ^announce, ^object} = CommonAPI.repeat(activity.id, user)
|
||||
end
|
||||
|
||||
test "favoriting a status twice returns ok, but without the like activity" do
|
||||
@ -360,7 +424,9 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||
|
||||
user = refresh_record(user)
|
||||
|
||||
assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user)
|
||||
id = activity.id
|
||||
|
||||
assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
|
||||
|
||||
user = refresh_record(user)
|
||||
|
||||
|
@ -335,26 +335,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_by_id_or_ap_id/1" do
|
||||
test "get activity by id" do
|
||||
activity = insert(:note_activity)
|
||||
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.id)
|
||||
assert note.id == activity.id
|
||||
end
|
||||
|
||||
test "get activity by ap_id" do
|
||||
activity = insert(:note_activity)
|
||||
%Pleroma.Activity{} = note = Utils.get_by_id_or_ap_id(activity.data["object"])
|
||||
assert note.id == activity.id
|
||||
end
|
||||
|
||||
test "get activity by object when type isn't `Create` " do
|
||||
activity = insert(:like_activity)
|
||||
%Pleroma.Activity{} = like = Utils.get_by_id_or_ap_id(activity.id)
|
||||
assert like.data["object"] == activity.data["object"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "to_master_date/1" do
|
||||
test "removes microseconds from date (NaiveDateTime)" do
|
||||
assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z"
|
||||
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
|
||||
describe "updating credentials" do
|
||||
setup do: oauth_access(["write:accounts"])
|
||||
setup :request_content_type
|
||||
|
||||
test "sets user settings in a generic way", %{conn: conn} do
|
||||
res_conn =
|
||||
@ -25,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
|
||||
|
||||
user = Repo.get(User, user_data["id"])
|
||||
@ -41,7 +42,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
|
||||
assert user_data["pleroma"]["settings_store"] ==
|
||||
%{
|
||||
@ -62,7 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
|
||||
assert user_data["pleroma"]["settings_store"] ==
|
||||
%{
|
||||
@ -79,7 +80,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
"note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.."
|
||||
})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert user_data["note"] ==
|
||||
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
|
||||
@ -90,7 +91,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
test "updates the user's locking status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["locked"] == true
|
||||
end
|
||||
|
||||
@ -100,21 +101,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
|
||||
|
||||
assert refresh_record(user).allow_following_move == false
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["allow_following_move"] == false
|
||||
end
|
||||
|
||||
test "updates the user's default scope", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data["source"]["privacy"] == "cofe"
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["privacy"] == "unlisted"
|
||||
end
|
||||
|
||||
test "updates the user's hide_followers status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_followers"] == true
|
||||
end
|
||||
|
||||
@ -122,12 +123,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"})
|
||||
|> json_response(:ok)
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"})
|
||||
|> json_response(:ok)
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
end
|
||||
|
||||
test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
|
||||
@ -137,7 +138,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
hide_follows_count: "true"
|
||||
})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_followers_count"] == true
|
||||
assert user_data["pleroma"]["hide_follows_count"] == true
|
||||
end
|
||||
@ -146,7 +147,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
response =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert response["pleroma"]["skip_thread_containment"] == true
|
||||
assert refresh_record(user).skip_thread_containment
|
||||
@ -155,28 +156,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
test "updates the user's hide_follows status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_follows"] == true
|
||||
end
|
||||
|
||||
test "updates the user's hide_favorites status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_favorites"] == true
|
||||
end
|
||||
|
||||
test "updates the user's show_role status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["pleroma"]["show_role"] == false
|
||||
end
|
||||
|
||||
test "updates the user's no_rich_text status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["pleroma"]["no_rich_text"] == true
|
||||
end
|
||||
|
||||
@ -184,7 +185,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
conn =
|
||||
patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["display_name"] == "markorepairs"
|
||||
end
|
||||
|
||||
@ -197,7 +198,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["avatar"] != User.avatar_url(user)
|
||||
end
|
||||
|
||||
@ -210,7 +211,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["header"] != User.banner_url(user)
|
||||
end
|
||||
|
||||
@ -226,7 +227,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
"pleroma_background_image" => new_header
|
||||
})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["pleroma"]["background_image"]
|
||||
end
|
||||
|
||||
@ -237,14 +238,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
for token <- [token1, token2] do
|
||||
conn =
|
||||
build_conn()
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||
|> patch("/api/v1/accounts/update_credentials", %{})
|
||||
|
||||
if token == token1 do
|
||||
assert %{"error" => "Insufficient permissions: write:accounts."} ==
|
||||
json_response(conn, 403)
|
||||
json_response_and_validate_schema(conn, 403)
|
||||
else
|
||||
assert json_response(conn, 200)
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -259,11 +261,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
"display_name" => name
|
||||
})
|
||||
|
||||
assert json_response(ret_conn, 200)
|
||||
assert json_response_and_validate_schema(ret_conn, 200)
|
||||
|
||||
conn = get(conn, "/api/v1/accounts/#{user.id}")
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert user_data["note"] == note
|
||||
assert user_data["display_name"] == name
|
||||
@ -279,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
account_data =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account_data["fields"] == [
|
||||
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
|
||||
@ -312,7 +314,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
conn
|
||||
|> put_req_header("content-type", "application/x-www-form-urlencoded")
|
||||
|> patch("/api/v1/accounts/update_credentials", fields)
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account["fields"] == [
|
||||
%{"name" => "foo", "value" => "bar"},
|
||||
@ -337,7 +339,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
account =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account["fields"] == [
|
||||
%{"name" => "foo", "value" => ""}
|
||||
@ -356,14 +358,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
|
||||
fields = [%{"name" => long_name, "value" => "bar"}]
|
||||
|
||||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
|
||||
Pleroma.Config.put([:instance, :max_account_fields], 1)
|
||||
|
||||
@ -375,7 +377,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
|
||||
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||
}
|
||||
|
||||
assert expected == json_response(conn, 200)
|
||||
assert expected == json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "creates an oauth app", %{conn: conn} do
|
||||
@ -55,6 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
|
||||
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||
}
|
||||
|
||||
assert expected == json_response(conn, 200)
|
||||
assert expected == json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
|
@ -4,16 +4,12 @@
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmojisResponse
|
||||
import OpenApiSpex.TestAssertions
|
||||
|
||||
test "with tags", %{conn: conn} do
|
||||
assert resp =
|
||||
conn
|
||||
|> get("/api/v1/custom_emojis")
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [emoji | _body] = resp
|
||||
assert Map.has_key?(emoji, "shortcode")
|
||||
@ -23,19 +19,5 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do
|
||||
assert Map.has_key?(emoji, "category")
|
||||
assert Map.has_key?(emoji, "url")
|
||||
assert Map.has_key?(emoji, "visible_in_picker")
|
||||
assert_schema(resp, "CustomEmojisResponse", ApiSpec.spec())
|
||||
assert_schema(emoji, "CustomEmoji", ApiSpec.spec())
|
||||
end
|
||||
|
||||
test "CustomEmoji example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = CustomEmoji.schema()
|
||||
assert_schema(schema.example, "CustomEmoji", api_spec)
|
||||
end
|
||||
|
||||
test "CustomEmojisResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = CustomEmojisResponse.schema()
|
||||
assert_schema(schema.example, "CustomEmojisResponse", api_spec)
|
||||
end
|
||||
end
|
||||
|
@ -6,11 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ApiSpec
|
||||
alias Pleroma.Web.ApiSpec.Schemas.DomainBlocksResponse
|
||||
|
||||
import Pleroma.Factory
|
||||
import OpenApiSpex.TestAssertions
|
||||
|
||||
test "blocking / unblocking a domain" do
|
||||
%{user: user, conn: conn} = oauth_access(["write:blocks"])
|
||||
@ -21,7 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(ret_conn, 200)
|
||||
assert %{} == json_response_and_validate_schema(ret_conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
assert User.blocks?(user, other_user)
|
||||
|
||||
@ -30,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
|
||||
|
||||
assert %{} = json_response(ret_conn, 200)
|
||||
assert %{} == json_response_and_validate_schema(ret_conn, 200)
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
refute User.blocks?(user, other_user)
|
||||
end
|
||||
@ -41,21 +38,10 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do
|
||||
{:ok, user} = User.block_domain(user, "bad.site")
|
||||
{:ok, user} = User.block_domain(user, "even.worse.site")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/domain_blocks")
|
||||
|
||||
domain_blocks = json_response(conn, 200)
|
||||
|
||||
assert "bad.site" in domain_blocks
|
||||
assert "even.worse.site" in domain_blocks
|
||||
assert_schema(domain_blocks, "DomainBlocksResponse", ApiSpec.spec())
|
||||
end
|
||||
|
||||
test "DomainBlocksResponse example matches schema" do
|
||||
api_spec = ApiSpec.spec()
|
||||
schema = DomainBlocksResponse.schema()
|
||||
assert_schema(schema.example, "DomainBlocksResponse", api_spec)
|
||||
assert ["even.worse.site", "bad.site"] ==
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/domain_blocks")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
end
|
||||
|
@ -302,6 +302,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
||||
assert [] == Repo.all(Activity)
|
||||
end
|
||||
|
||||
test "ignores nil values", %{conn: conn} do
|
||||
conn =
|
||||
post(conn, "/api/v1/statuses", %{
|
||||
"status" => "not scheduled",
|
||||
"scheduled_at" => nil
|
||||
})
|
||||
|
||||
assert result = json_response(conn, 200)
|
||||
assert Activity.get_by_id(result["id"])
|
||||
end
|
||||
|
||||
test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do
|
||||
scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
|
||||
|
||||
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MongooseIMController do
|
||||
test "/user_exists", %{conn: conn} do
|
||||
_user = insert(:user, nickname: "lain")
|
||||
_remote_user = insert(:user, nickname: "alice", local: false)
|
||||
_deactivated_user = insert(:user, nickname: "konata", deactivated: true)
|
||||
|
||||
res =
|
||||
conn
|
||||
@ -30,11 +31,25 @@ defmodule Pleroma.Web.MongooseIMController do
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :user_exists), user: "konata")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
end
|
||||
|
||||
test "/check_password", %{conn: conn} do
|
||||
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("cool"))
|
||||
|
||||
_deactivated_user =
|
||||
insert(:user,
|
||||
nickname: "konata",
|
||||
deactivated: true,
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("cool")
|
||||
)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
|
||||
@ -49,6 +64,13 @@ defmodule Pleroma.Web.MongooseIMController do
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
|
||||
|
@ -18,11 +18,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
test "it registers a new user and returns the user." do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -35,12 +35,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
test "it registers a new user with empty string in bio and returns the user." do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -60,12 +60,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
end
|
||||
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -87,23 +87,23 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
test "it registers a new user and parses mentions in the bio" do
|
||||
data1 = %{
|
||||
"nickname" => "john",
|
||||
"email" => "john@gmail.com",
|
||||
"fullname" => "John Doe",
|
||||
"bio" => "test",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "john",
|
||||
:email => "john@gmail.com",
|
||||
:fullname => "John Doe",
|
||||
:bio => "test",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user1} = TwitterAPI.register_user(data1)
|
||||
|
||||
data2 = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "@john test",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "@john test",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user2} = TwitterAPI.register_user(data2)
|
||||
@ -123,13 +123,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
{:ok, invite} = UserInviteToken.create_invite()
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -145,13 +145,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
test "returns error on invalid token" do
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => "DudeLetMeInImAFairy"
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => "DudeLetMeInImAFairy"
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -165,13 +165,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
UserInviteToken.update_invite!(invite, used: true)
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -186,16 +186,16 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
setup do
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees"
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees"
|
||||
}
|
||||
|
||||
check_fn = fn invite ->
|
||||
data = Map.put(data, "token", invite.token)
|
||||
data = Map.put(data, :token, invite.token)
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
fetched_user = User.get_cached_by_nickname("vinny")
|
||||
|
||||
@ -250,13 +250,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
UserInviteToken.update_invite!(invite, uses: 99)
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -269,13 +269,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
AccountView.render("show.json", %{user: fetched_user})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -292,13 +292,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -317,13 +317,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
UserInviteToken.update_invite!(invite, uses: 99)
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
@ -335,13 +335,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
AccountView.render("show.json", %{user: fetched_user})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -355,13 +355,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -377,13 +377,13 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
UserInviteToken.update_invite!(invite, uses: 100)
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
@ -395,11 +395,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
|
||||
test "it returns the error on registration problems" do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "close the world.",
|
||||
"password" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "close the world.",
|
||||
:password => "bear"
|
||||
}
|
||||
|
||||
{:error, error_object} = TwitterAPI.register_user(data)
|
||||
|
Loading…
Reference in New Issue
Block a user