Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
This commit is contained in:
commit
205313e541
|
@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
||||||
- Mastodon API: Add support for filtering replies in public and home timelines
|
- Mastodon API: Add support for filtering replies in public and home timelines
|
||||||
- Admin API: endpoints for create/update/delete OAuth Apps.
|
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||||
|
- Admin API: endpoint for status view.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -37,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
|
- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
|
||||||
- Fix follower/blocks import when nicknames starts with @
|
- Fix follower/blocks import when nicknames starts with @
|
||||||
- Filtering of push notifications on activities from blocked domains
|
- Filtering of push notifications on activities from blocked domains
|
||||||
|
- Resolving Peertube accounts with Webfinger
|
||||||
|
|
||||||
## [unreleased-patch]
|
## [unreleased-patch]
|
||||||
### Security
|
### Security
|
||||||
|
@ -47,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Logger configuration through AdminFE
|
- Logger configuration through AdminFE
|
||||||
- HTTP Basic Authentication permissions issue
|
- HTTP Basic Authentication permissions issue
|
||||||
- ObjectAgePolicy didn't filter out old messages
|
- ObjectAgePolicy didn't filter out old messages
|
||||||
|
- Transmogrifier: Keep object sensitive settings for outgoing representation (AP C2S)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- NodeInfo: ObjectAgePolicy settings to the `federation` list.
|
- NodeInfo: ObjectAgePolicy settings to the `federation` list.
|
||||||
|
|
|
@ -653,6 +653,8 @@
|
||||||
profiles: %{local: false, remote: false},
|
profiles: %{local: false, remote: false},
|
||||||
activities: %{local: false, remote: false}
|
activities: %{local: false, remote: false}
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -2247,6 +2247,7 @@
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :active,
|
key: :active,
|
||||||
|
label: "Enabled",
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Globally enable or disable digest emails"
|
description: "Globally enable or disable digest emails"
|
||||||
},
|
},
|
||||||
|
@ -3194,5 +3195,19 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Web.ApiSpec.CastAndValidate,
|
||||||
|
type: :group,
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :strict,
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Enables strict input validation (useful in development, not recommended in production)",
|
||||||
|
suggestions: [false]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -52,6 +52,8 @@
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
pool_size: 10
|
pool_size: 10
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||||
|
|
||||||
if File.exists?("./config/dev.secret.exs") do
|
if File.exists?("./config/dev.secret.exs") do
|
||||||
import_config "dev.secret.exs"
|
import_config "dev.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -96,6 +96,8 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false
|
config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||||
|
|
||||||
if File.exists?("./config/test.secret.exs") do
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -755,6 +755,17 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
||||||
- On success: `204`, empty response
|
- On success: `204`, empty response
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/statuses/:id`
|
||||||
|
|
||||||
|
### Show status by id
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `id`: required, status id
|
||||||
|
- Response:
|
||||||
|
- On failure:
|
||||||
|
- 404 Not Found `"Not Found"`
|
||||||
|
- On success: JSON, Mastodon Status entity
|
||||||
|
|
||||||
## `PUT /api/pleroma/admin/statuses/:id`
|
## `PUT /api/pleroma/admin/statuses/:id`
|
||||||
|
|
||||||
### Change the scope of an individual reported status
|
### Change the scope of an individual reported status
|
||||||
|
|
|
@ -925,3 +925,7 @@ Restrict access for unauthenticated users to timelines (public and federate), us
|
||||||
* `activities` - statuses
|
* `activities` - statuses
|
||||||
* `local`
|
* `local`
|
||||||
* `remote`
|
* `remote`
|
||||||
|
|
||||||
|
## Pleroma.Web.ApiSpec.CastAndValidate
|
||||||
|
|
||||||
|
* `:strict` a boolean, enables strict input validation (useful in development, not recommended in production). Defaults to `false`.
|
||||||
|
|
|
@ -128,7 +128,7 @@ def for_user(user, params \\ %{}) do
|
||||||
|> Pleroma.Pagination.fetch_paginated(params)
|
|> Pleroma.Pagination.fetch_paginated(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def restrict_recipients(query, user, %{"recipients" => user_ids}) do
|
def restrict_recipients(query, user, %{recipients: user_ids}) do
|
||||||
user_binary_ids =
|
user_binary_ids =
|
||||||
[user.id | user_ids]
|
[user.id | user_ids]
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|
@ -172,7 +172,7 @@ def for_user_with_last_activity_id(user, params \\ %{}) do
|
||||||
| last_activity_id: activity_id
|
| last_activity_id: activity_id
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|> Enum.filter(& &1.last_activity_id)
|
|> Enum.reject(&is_nil(&1.last_activity_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(_, _ \\ [])
|
def get(_, _ \\ [])
|
||||||
|
|
|
@ -89,11 +89,10 @@ def delete(%Pleroma.Filter{id: filter_key} = filter) when is_nil(filter_key) do
|
||||||
|> Repo.delete()
|
|> Repo.delete()
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%Pleroma.Filter{} = filter) do
|
def update(%Pleroma.Filter{} = filter, params) do
|
||||||
destination = Map.from_struct(filter)
|
filter
|
||||||
|
|> cast(params, [:phrase, :context, :hide, :expires_at, :whole_word])
|
||||||
Pleroma.Filter.get(filter.filter_id, %{id: filter.user_id})
|
|> validate_required([:phrase, :context])
|
||||||
|> cast(destination, [:phrase, :context, :hide, :expires_at, :whole_word])
|
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,22 +19,7 @@ def perform(%{assigns: %{user: %User{}}} = conn, _) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(conn, options) do
|
def perform(conn, _) do
|
||||||
perform =
|
|
||||||
cond do
|
|
||||||
options[:if_func] -> options[:if_func].()
|
|
||||||
options[:unless_func] -> !options[:unless_func].()
|
|
||||||
true -> true
|
|
||||||
end
|
|
||||||
|
|
||||||
if perform do
|
|
||||||
fail(conn)
|
|
||||||
else
|
|
||||||
conn
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def fail(conn) do
|
|
||||||
conn
|
conn
|
||||||
|> render_error(:forbidden, "Invalid credentials.")
|
|> render_error(:forbidden, "Invalid credentials.")
|
||||||
|> halt()
|
|> halt()
|
||||||
|
|
|
@ -19,6 +19,9 @@ def call(conn, _opts) do
|
||||||
|
|
||||||
def federating?, do: Pleroma.Config.get([:instance, :federating])
|
def federating?, do: Pleroma.Config.get([:instance, :federating])
|
||||||
|
|
||||||
|
# Definition for the use in :if_func / :unless_func plug options
|
||||||
|
def federating?(_conn), do: federating?()
|
||||||
|
|
||||||
defp fail(conn) do
|
defp fail(conn) do
|
||||||
conn
|
conn
|
||||||
|> put_status(404)
|
|> put_status(404)
|
||||||
|
|
|
@ -91,7 +91,7 @@ def calculate_stat_data do
|
||||||
peers: peers,
|
peers: peers,
|
||||||
stats: %{
|
stats: %{
|
||||||
domain_count: domain_count,
|
domain_count: domain_count,
|
||||||
status_count: status_count,
|
status_count: status_count || 0,
|
||||||
user_count: user_count
|
user_count: user_count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,6 @@ defmodule Pleroma.User do
|
||||||
field(:is_admin, :boolean, default: false)
|
field(:is_admin, :boolean, default: false)
|
||||||
field(:show_role, :boolean, default: true)
|
field(:show_role, :boolean, default: true)
|
||||||
field(:settings, :map, default: nil)
|
field(:settings, :map, default: nil)
|
||||||
field(:magic_key, :string, default: nil)
|
|
||||||
field(:uri, Types.Uri, default: nil)
|
field(:uri, Types.Uri, default: nil)
|
||||||
field(:hide_followers_count, :boolean, default: false)
|
field(:hide_followers_count, :boolean, default: false)
|
||||||
field(:hide_follows_count, :boolean, default: false)
|
field(:hide_follows_count, :boolean, default: false)
|
||||||
|
@ -387,7 +386,6 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do
|
||||||
:banner,
|
:banner,
|
||||||
:locked,
|
:locked,
|
||||||
:last_refreshed_at,
|
:last_refreshed_at,
|
||||||
:magic_key,
|
|
||||||
:uri,
|
:uri,
|
||||||
:follower_address,
|
:follower_address,
|
||||||
:following_address,
|
:following_address,
|
||||||
|
|
|
@ -45,6 +45,7 @@ defmodule Pleroma.User.Query do
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
|
exclude_service_users: boolean(),
|
||||||
followers: User.t(),
|
followers: User.t(),
|
||||||
friends: User.t(),
|
friends: User.t(),
|
||||||
recipients_from_activity: [String.t()],
|
recipients_from_activity: [String.t()],
|
||||||
|
@ -88,6 +89,10 @@ defp compose_query({key, value}, query)
|
||||||
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
where(query, [u], ilike(field(u, ^key), ^"%#{value}%"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:exclude_service_users, _}, query) do
|
||||||
|
where(query, [u], not like(u.ap_id, "%/relay") and not like(u.ap_id, "%/internal/fetch"))
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({key, value}, query)
|
defp compose_query({key, value}, query)
|
||||||
when key in @equal_criteria and not_empty_string(value) do
|
when key in @equal_criteria and not_empty_string(value) do
|
||||||
where(query, [u], ^[{key, value}])
|
where(query, [u], ^[{key, value}])
|
||||||
|
@ -98,7 +103,7 @@ defp compose_query({key, values}, query) when key in @contains_criteria and is_l
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 do
|
defp compose_query({:tags, tags}, query) when is_list(tags) and length(tags) > 0 do
|
||||||
Enum.reduce(tags, query, &prepare_tag_criteria/2)
|
where(query, [u], fragment("? && ?", u.tags, ^tags))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:is_admin, _}, query) do
|
defp compose_query({:is_admin, _}, query) do
|
||||||
|
@ -192,10 +197,6 @@ defp compose_query({:limit, limit}, query) do
|
||||||
|
|
||||||
defp compose_query(_unsupported_param, query), do: query
|
defp compose_query(_unsupported_param, query), do: query
|
||||||
|
|
||||||
defp prepare_tag_criteria(tag, query) do
|
|
||||||
or_where(query, [u], fragment("? = any(?)", ^tag, u.tags))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp location_query(query, local) do
|
defp location_query(query, local) do
|
||||||
where(query, [u], u.local == ^local)
|
where(query, [u], u.local == ^local)
|
||||||
|> where([u], not is_nil(u.nickname))
|
|> where([u], not is_nil(u.nickname))
|
||||||
|
|
|
@ -1550,21 +1550,34 @@ def fetch_follow_information_for_user(user) do
|
||||||
defp normalize_counter(counter) when is_integer(counter), do: counter
|
defp normalize_counter(counter) when is_integer(counter), do: counter
|
||||||
defp normalize_counter(_), do: 0
|
defp normalize_counter(_), do: 0
|
||||||
|
|
||||||
defp maybe_update_follow_information(data) do
|
def maybe_update_follow_information(user_data) do
|
||||||
with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
|
with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
|
||||||
{:ok, info} <- fetch_follow_information_for_user(data) do
|
{_, true} <- {:user_type_check, user_data[:type] in ["Person", "Service"]},
|
||||||
info = Map.merge(data[:info] || %{}, info)
|
{_, true} <-
|
||||||
Map.put(data, :info, info)
|
{:collections_available,
|
||||||
|
!!(user_data[:following_address] && user_data[:follower_address])},
|
||||||
|
{:ok, info} <-
|
||||||
|
fetch_follow_information_for_user(user_data) do
|
||||||
|
info = Map.merge(user_data[:info] || %{}, info)
|
||||||
|
|
||||||
|
user_data
|
||||||
|
|> Map.put(:info, info)
|
||||||
else
|
else
|
||||||
|
{:user_type_check, false} ->
|
||||||
|
user_data
|
||||||
|
|
||||||
|
{:collections_available, false} ->
|
||||||
|
user_data
|
||||||
|
|
||||||
{:enabled, false} ->
|
{:enabled, false} ->
|
||||||
data
|
user_data
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
Logger.error(
|
Logger.error(
|
||||||
"Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
|
"Follower/Following counter update for #{user_data.ap_id} failed.\n" <> inspect(e)
|
||||||
)
|
)
|
||||||
|
|
||||||
data
|
user_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
EnsureAuthenticatedPlug,
|
EnsureAuthenticatedPlug,
|
||||||
[unless_func: &FederatingPlug.federating?/0] when action not in @federating_only_actions
|
[unless_func: &FederatingPlug.federating?/1] when action not in @federating_only_actions
|
||||||
)
|
)
|
||||||
|
|
||||||
# Note: :following and :followers must be served even without authentication (as via :api)
|
# Note: :following and :followers must be served even without authentication (as via :api)
|
||||||
|
|
|
@ -1205,6 +1205,10 @@ def set_conversation(object) do
|
||||||
Map.put(object, "conversation", object["context"])
|
Map.put(object, "conversation", object["context"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_sensitive(%{"sensitive" => true} = object) do
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
def set_sensitive(object) do
|
def set_sensitive(object) do
|
||||||
tags = object["tag"] || []
|
tags = object["tag"] || []
|
||||||
Map.put(object, "sensitive", "nsfw" in tags)
|
Map.put(object, "sensitive", "nsfw" in tags)
|
||||||
|
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"], admin: true}
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses]
|
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -392,29 +392,12 @@ def list_users(conn, params) do
|
||||||
email: params["email"]
|
email: params["email"]
|
||||||
}
|
}
|
||||||
|
|
||||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
|
||||||
{:ok, users, count} <- filter_service_users(users, count),
|
json(
|
||||||
do:
|
conn,
|
||||||
conn
|
AccountView.render("index.json", users: users, count: count, page_size: page_size)
|
||||||
|> json(
|
)
|
||||||
AccountView.render("index.json",
|
end
|
||||||
users: users,
|
|
||||||
count: count,
|
|
||||||
page_size: page_size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp filter_service_users(users, count) do
|
|
||||||
filtered_users = Enum.reject(users, &service_user?/1)
|
|
||||||
count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
|
|
||||||
|
|
||||||
{:ok, filtered_users, count}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp service_user?(user) do
|
|
||||||
String.match?(user.ap_id, ~r/.*\/relay$/) or
|
|
||||||
String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@filters ~w(local external active deactivated is_admin is_moderator)
|
@filters ~w(local external active deactivated is_admin is_moderator)
|
||||||
|
@ -837,6 +820,16 @@ def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
|
||||||
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def status_show(conn, %{"id" => id}) do
|
||||||
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> render("show.json", %{activity: activity})
|
||||||
|
else
|
||||||
|
_ -> errors(conn, {:error, :not_found})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||||
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
||||||
|
|
|
@ -21,6 +21,7 @@ def user(params \\ %{}) do
|
||||||
query =
|
query =
|
||||||
params
|
params
|
||||||
|> Map.drop([:page, :page_size])
|
|> Map.drop([:page, :page_size])
|
||||||
|
|> Map.put(:exclude_service_users, true)
|
||||||
|> User.Query.build()
|
|> User.Query.build()
|
||||||
|> order_by([u], u.nickname)
|
|> order_by([u], u.nickname)
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,12 @@ def spec do
|
||||||
password: %OpenApiSpex.OAuthFlow{
|
password: %OpenApiSpex.OAuthFlow{
|
||||||
authorizationUrl: "/oauth/authorize",
|
authorizationUrl: "/oauth/authorize",
|
||||||
tokenUrl: "/oauth/token",
|
tokenUrl: "/oauth/token",
|
||||||
scopes: %{"read" => "read", "write" => "write", "follow" => "follow"}
|
scopes: %{
|
||||||
|
"read" => "read",
|
||||||
|
"write" => "write",
|
||||||
|
"follow" => "follow",
|
||||||
|
"push" => "push"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2019-2020 Moxley Stratton, Mike Buhot <https://github.com/open-api-spex/open_api_spex>, MPL-2.0
|
||||||
|
# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.CastAndValidate do
|
||||||
|
@moduledoc """
|
||||||
|
This plug is based on [`OpenApiSpex.Plug.CastAndValidate`]
|
||||||
|
(https://github.com/open-api-spex/open_api_spex/blob/master/lib/open_api_spex/plug/cast_and_validate.ex).
|
||||||
|
The main difference is ignoring unexpected query params instead of throwing
|
||||||
|
an error and a config option (`[Pleroma.Web.ApiSpec.CastAndValidate, :strict]`)
|
||||||
|
to disable this behavior. Also, the default rendering error module
|
||||||
|
is `Pleroma.Web.ApiSpec.RenderError`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
alias Plug.Conn
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
def init(opts) do
|
||||||
|
opts
|
||||||
|
|> Map.new()
|
||||||
|
|> Map.put_new(:render_error, Pleroma.Web.ApiSpec.RenderError)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
def call(%{private: %{open_api_spex: private_data}} = conn, %{
|
||||||
|
operation_id: operation_id,
|
||||||
|
render_error: render_error
|
||||||
|
}) do
|
||||||
|
spec = private_data.spec
|
||||||
|
operation = private_data.operation_lookup[operation_id]
|
||||||
|
|
||||||
|
content_type =
|
||||||
|
case Conn.get_req_header(conn, "content-type") do
|
||||||
|
[header_value | _] ->
|
||||||
|
header_value
|
||||||
|
|> String.split(";")
|
||||||
|
|> List.first()
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private_data = Map.put(private_data, :operation_id, operation_id)
|
||||||
|
conn = Conn.put_private(conn, :open_api_spex, private_data)
|
||||||
|
|
||||||
|
case cast_and_validate(spec, operation, conn, content_type, strict?()) do
|
||||||
|
{:ok, conn} ->
|
||||||
|
conn
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
opts = render_error.init(reason)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> render_error.call(opts)
|
||||||
|
|> Plug.Conn.halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(
|
||||||
|
%{
|
||||||
|
private: %{
|
||||||
|
phoenix_controller: controller,
|
||||||
|
phoenix_action: action,
|
||||||
|
open_api_spex: private_data
|
||||||
|
}
|
||||||
|
} = conn,
|
||||||
|
opts
|
||||||
|
) do
|
||||||
|
operation =
|
||||||
|
case private_data.operation_lookup[{controller, action}] do
|
||||||
|
nil ->
|
||||||
|
operation_id = controller.open_api_operation(action).operationId
|
||||||
|
operation = private_data.operation_lookup[operation_id]
|
||||||
|
|
||||||
|
operation_lookup =
|
||||||
|
private_data.operation_lookup
|
||||||
|
|> Map.put({controller, action}, operation)
|
||||||
|
|
||||||
|
OpenApiSpex.Plug.Cache.adapter().put(
|
||||||
|
private_data.spec_module,
|
||||||
|
{private_data.spec, operation_lookup}
|
||||||
|
)
|
||||||
|
|
||||||
|
operation
|
||||||
|
|
||||||
|
operation ->
|
||||||
|
operation
|
||||||
|
end
|
||||||
|
|
||||||
|
if operation.operationId do
|
||||||
|
call(conn, Map.put(opts, :operation_id, operation.operationId))
|
||||||
|
else
|
||||||
|
raise "operationId was not found in action API spec"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts)
|
||||||
|
|
||||||
|
defp cast_and_validate(spec, operation, conn, content_type, true = _strict) do
|
||||||
|
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do
|
||||||
|
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
|
||||||
|
{:ok, conn} ->
|
||||||
|
{:ok, conn}
|
||||||
|
|
||||||
|
# Remove unexpected query params and cast/validate again
|
||||||
|
{:error, errors} ->
|
||||||
|
query_params =
|
||||||
|
Enum.reduce(errors, conn.query_params, fn
|
||||||
|
%{reason: :unexpected_field, name: name, path: [name]}, params ->
|
||||||
|
Map.delete(params, name)
|
||||||
|
|
||||||
|
%{reason: :invalid_enum, name: nil, path: path, value: value}, params ->
|
||||||
|
path = path |> Enum.reverse() |> tl() |> Enum.reverse() |> list_items_to_string()
|
||||||
|
update_in(params, path, &List.delete(&1, value))
|
||||||
|
|
||||||
|
_, params ->
|
||||||
|
params
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn = %Conn{conn | query_params: query_params}
|
||||||
|
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp list_items_to_string(list) do
|
||||||
|
Enum.map(list, fn
|
||||||
|
i when is_atom(i) -> to_string(i)
|
||||||
|
i -> i
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp strict?, do: Pleroma.Config.get([__MODULE__, :strict], false)
|
||||||
|
end
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.List
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||||
|
|
||||||
|
@ -646,28 +647,12 @@ defp mute_request do
|
||||||
}
|
}
|
||||||
end
|
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
|
defp array_of_lists do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "ArrayOfLists",
|
title: "ArrayOfLists",
|
||||||
description: "Response schema for lists",
|
description: "Response schema for lists",
|
||||||
type: :array,
|
type: :array,
|
||||||
items: list(),
|
items: List,
|
||||||
example: [
|
example: [
|
||||||
%{"id" => "123", "title" => "my list"},
|
%{"id" => "123", "title" => "my list"},
|
||||||
%{"id" => "1337", "title" => "anotehr list"}
|
%{"id" => "1337", "title" => "anotehr list"}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# 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.ConversationOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Conversation
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Conversations"],
|
||||||
|
summary: "Show conversation",
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
operationId: "ConversationController.index",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:recipients,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :array, items: FlakeID},
|
||||||
|
"Only return conversations with the given recipients (a list of user ids)"
|
||||||
|
)
|
||||||
|
| pagination_params()
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of Conversation", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: Conversation,
|
||||||
|
example: [Conversation.schema().example]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def mark_as_read_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Conversations"],
|
||||||
|
summary: "Mark as read",
|
||||||
|
operationId: "ConversationController.mark_as_read",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:id, :path, :string, "Conversation ID",
|
||||||
|
example: "123",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["write:conversations"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Conversation", "application/json", Conversation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,227 @@
|
||||||
|
# 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.FilterOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["apps"],
|
||||||
|
summary: "View all filters",
|
||||||
|
operationId: "FilterController.index",
|
||||||
|
security: [%{"oAuth" => ["read:filters"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Filters", "application/json", array_of_filters())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["apps"],
|
||||||
|
summary: "Create a filter",
|
||||||
|
operationId: "FilterController.create",
|
||||||
|
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||||
|
security: [%{"oAuth" => ["write:filters"]}],
|
||||||
|
responses: %{200 => Operation.response("Filter", "application/json", filter())}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["apps"],
|
||||||
|
summary: "View all filters",
|
||||||
|
parameters: [id_param()],
|
||||||
|
operationId: "FilterController.show",
|
||||||
|
security: [%{"oAuth" => ["read:filters"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Filter", "application/json", filter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["apps"],
|
||||||
|
summary: "Update a filter",
|
||||||
|
parameters: [id_param()],
|
||||||
|
operationId: "FilterController.update",
|
||||||
|
requestBody: Helpers.request_body("Parameters", update_request(), required: true),
|
||||||
|
security: [%{"oAuth" => ["write:filters"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Filter", "application/json", filter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["apps"],
|
||||||
|
summary: "Remove a filter",
|
||||||
|
parameters: [id_param()],
|
||||||
|
operationId: "FilterController.delete",
|
||||||
|
security: [%{"oAuth" => ["write:filters"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Filter", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Empty object"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, :string, "Filter ID", example: "123", required: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp filter do
|
||||||
|
%Schema{
|
||||||
|
title: "Filter",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
phrase: %Schema{type: :string, description: "The text to be filtered"},
|
||||||
|
context: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string, enum: ["home", "notifications", "public", "thread"]},
|
||||||
|
description: "The contexts in which the filter should be applied."
|
||||||
|
},
|
||||||
|
expires_at: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :"date-time",
|
||||||
|
description:
|
||||||
|
"When the filter should no longer be applied. String (ISO 8601 Datetime), or null if the filter does not expire.",
|
||||||
|
nullable: true
|
||||||
|
},
|
||||||
|
irreversible: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Should matching entities in home and notifications be dropped by the server?"
|
||||||
|
},
|
||||||
|
whole_word: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Should the filter consider word boundaries?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "5580",
|
||||||
|
"phrase" => "@twitter.com",
|
||||||
|
"context" => [
|
||||||
|
"home",
|
||||||
|
"notifications",
|
||||||
|
"public",
|
||||||
|
"thread"
|
||||||
|
],
|
||||||
|
"whole_word" => false,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"irreversible" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp array_of_filters do
|
||||||
|
%Schema{
|
||||||
|
title: "ArrayOfFilters",
|
||||||
|
description: "Array of Filters",
|
||||||
|
type: :array,
|
||||||
|
items: filter(),
|
||||||
|
example: [
|
||||||
|
%{
|
||||||
|
"id" => "5580",
|
||||||
|
"phrase" => "@twitter.com",
|
||||||
|
"context" => [
|
||||||
|
"home",
|
||||||
|
"notifications",
|
||||||
|
"public",
|
||||||
|
"thread"
|
||||||
|
],
|
||||||
|
"whole_word" => false,
|
||||||
|
"expires_at" => nil,
|
||||||
|
"irreversible" => true
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"id" => "6191",
|
||||||
|
"phrase" => ":eurovision2019:",
|
||||||
|
"context" => [
|
||||||
|
"home"
|
||||||
|
],
|
||||||
|
"whole_word" => true,
|
||||||
|
"expires_at" => "2019-05-21T13:47:31.333Z",
|
||||||
|
"irreversible" => false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
title: "FilterCreateRequest",
|
||||||
|
allOf: [
|
||||||
|
update_request(),
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
irreversible: %Schema{
|
||||||
|
type: :bolean,
|
||||||
|
description:
|
||||||
|
"Should the server irreversibly drop matching entities from home and notifications?",
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
example: %{
|
||||||
|
"phrase" => "knights",
|
||||||
|
"context" => ["home"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "FilterUpdateRequest",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
phrase: %Schema{type: :string, description: "The text to be filtered"},
|
||||||
|
context: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string, enum: ["home", "notifications", "public", "thread"]},
|
||||||
|
description:
|
||||||
|
"Array of enumerable strings `home`, `notifications`, `public`, `thread`. At least one context must be specified."
|
||||||
|
},
|
||||||
|
irreversible: %Schema{
|
||||||
|
type: :bolean,
|
||||||
|
description:
|
||||||
|
"Should the server irreversibly drop matching entities from home and notifications?"
|
||||||
|
},
|
||||||
|
whole_word: %Schema{
|
||||||
|
type: :bolean,
|
||||||
|
description: "Consider word boundaries?",
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
# TODO: probably should implement filter expiration
|
||||||
|
# expires_in: %Schema{
|
||||||
|
# type: :string,
|
||||||
|
# format: :"date-time",
|
||||||
|
# description:
|
||||||
|
# "ISO 8601 Datetime for when the filter expires. Otherwise,
|
||||||
|
# null for a filter that doesn't expire."
|
||||||
|
# }
|
||||||
|
},
|
||||||
|
required: [:phrase, :context],
|
||||||
|
example: %{
|
||||||
|
"phrase" => "knights",
|
||||||
|
"context" => ["home"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,65 @@
|
||||||
|
# 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.FollowRequestOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Follow Requests"],
|
||||||
|
summary: "Pending Follows",
|
||||||
|
security: [%{"oAuth" => ["read:follows", "follow"]}],
|
||||||
|
operationId: "FollowRequestController.index",
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of Account", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: Account,
|
||||||
|
example: [Account.schema().example]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Follow Requests"],
|
||||||
|
summary: "Accept Follow",
|
||||||
|
operationId: "FollowRequestController.authorize",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def reject_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Follow Requests"],
|
||||||
|
summary: "Reject Follow",
|
||||||
|
operationId: "FollowRequestController.reject",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, :string, "Conversation ID",
|
||||||
|
example: "123",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,169 @@
|
||||||
|
# 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.InstanceOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Instance"],
|
||||||
|
summary: "Fetch instance",
|
||||||
|
description: "Information about the server",
|
||||||
|
operationId: "InstanceController.show",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Instance", "application/json", instance())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def peers_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Instance"],
|
||||||
|
summary: "List of known hosts",
|
||||||
|
operationId: "InstanceController.peers",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Array of domains", "application/json", array_of_domains())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp instance do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
uri: %Schema{type: :string, description: "The domain name of the instance"},
|
||||||
|
title: %Schema{type: :string, description: "The title of the website"},
|
||||||
|
description: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Admin-defined description of the Pleroma site"
|
||||||
|
},
|
||||||
|
version: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "The version of Pleroma installed on the instance"
|
||||||
|
},
|
||||||
|
email: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "An email that may be contacted for any inquiries",
|
||||||
|
format: :email
|
||||||
|
},
|
||||||
|
urls: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "URLs of interest for clients apps",
|
||||||
|
properties: %{
|
||||||
|
streaming_api: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Websockets address for push streaming"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stats: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Statistics about how much information the instance contains",
|
||||||
|
properties: %{
|
||||||
|
user_count: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Users registered on this instance"
|
||||||
|
},
|
||||||
|
status_count: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Statuses authored by users on instance"
|
||||||
|
},
|
||||||
|
domain_count: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Domains federated with this instance"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
thumbnail: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Banner image for the website",
|
||||||
|
nullable: true
|
||||||
|
},
|
||||||
|
languages: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string},
|
||||||
|
description: "Primary langauges of the website and its staff"
|
||||||
|
},
|
||||||
|
registrations: %Schema{type: :boolean, description: "Whether registrations are enabled"},
|
||||||
|
# Extra (not present in Mastodon):
|
||||||
|
max_toot_chars: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: ": Posts character limit (CW/Subject included in the counter)"
|
||||||
|
},
|
||||||
|
poll_limits: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "A map with poll limits for local polls",
|
||||||
|
properties: %{
|
||||||
|
max_options: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Maximum number of options."
|
||||||
|
},
|
||||||
|
max_option_chars: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Maximum number of characters per option."
|
||||||
|
},
|
||||||
|
min_expiration: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Minimum expiration time (in seconds)."
|
||||||
|
},
|
||||||
|
max_expiration: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Maximum expiration time (in seconds)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
upload_limit: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "File size limit of uploads (except for avatar, background, banner)"
|
||||||
|
},
|
||||||
|
avatar_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
||||||
|
background_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
||||||
|
banner_upload_limit: %Schema{type: :integer, description: "The title of the website"}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"avatar_upload_limit" => 2_000_000,
|
||||||
|
"background_upload_limit" => 4_000_000,
|
||||||
|
"banner_upload_limit" => 4_000_000,
|
||||||
|
"description" => "A Pleroma instance, an alternative fediverse server",
|
||||||
|
"email" => "lain@lain.com",
|
||||||
|
"languages" => ["en"],
|
||||||
|
"max_toot_chars" => 5000,
|
||||||
|
"poll_limits" => %{
|
||||||
|
"max_expiration" => 31_536_000,
|
||||||
|
"max_option_chars" => 200,
|
||||||
|
"max_options" => 20,
|
||||||
|
"min_expiration" => 0
|
||||||
|
},
|
||||||
|
"registrations" => false,
|
||||||
|
"stats" => %{
|
||||||
|
"domain_count" => 2996,
|
||||||
|
"status_count" => 15_802,
|
||||||
|
"user_count" => 5
|
||||||
|
},
|
||||||
|
"thumbnail" => "https://lain.com/instance/thumbnail.jpeg",
|
||||||
|
"title" => "lain.com",
|
||||||
|
"upload_limit" => 16_000_000,
|
||||||
|
"uri" => "https://lain.com",
|
||||||
|
"urls" => %{
|
||||||
|
"streaming_api" => "wss://lain.com"
|
||||||
|
},
|
||||||
|
"version" => "2.7.2 (compatible; Pleroma 2.0.50-536-g25eec6d7-develop)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp array_of_domains do
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string},
|
||||||
|
example: ["pleroma.site", "lain.com", "bikeshed.party"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,188 @@
|
||||||
|
# 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.ListOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.List
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Show user's lists",
|
||||||
|
description: "Fetch all lists that the user owns",
|
||||||
|
security: [%{"oAuth" => ["read:lists"]}],
|
||||||
|
operationId: "ListController.index",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Array of List", "application/json", array_of_lists())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Create a list",
|
||||||
|
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
|
||||||
|
operationId: "ListController.create",
|
||||||
|
requestBody: create_update_request(),
|
||||||
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("List", "application/json", List),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Show a single list",
|
||||||
|
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
|
||||||
|
operationId: "ListController.show",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("List", "application/json", List),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Update a list",
|
||||||
|
description: "Change the title of a list",
|
||||||
|
operationId: "ListController.update",
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody: create_update_request(),
|
||||||
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("List", "application/json", List),
|
||||||
|
422 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Delete a list",
|
||||||
|
operationId: "ListController.delete",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_accounts_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "View accounts in list",
|
||||||
|
operationId: "ListController.list_accounts",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of Account", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: Account
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_to_list_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Add accounts to list",
|
||||||
|
description: "Add accounts to the given list.",
|
||||||
|
operationId: "ListController.add_to_list",
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody: add_remove_accounts_request(),
|
||||||
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_from_list_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Lists"],
|
||||||
|
summary: "Remove accounts from list",
|
||||||
|
operationId: "ListController.remove_from_list",
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody: add_remove_accounts_request(),
|
||||||
|
security: [%{"oAuth" => ["write:lists"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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" => "another list"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, :string, "List ID",
|
||||||
|
example: "123",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_update_request do
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for creating or updating a List",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
title: %Schema{type: :string, description: "List title"}
|
||||||
|
},
|
||||||
|
required: [:title]
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_remove_accounts_request do
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
description: "POST body for adding/removing accounts to/from a List",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
account_ids: %Schema{type: :array, description: "Array of account IDs", items: FlakeID}
|
||||||
|
},
|
||||||
|
required: [:account_ids]
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,140 @@
|
||||||
|
# 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.MarkerOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Markers"],
|
||||||
|
summary: "Get saved timeline position",
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
operationId: "MarkerController.index",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:timeline,
|
||||||
|
:query,
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string, enum: ["home", "notifications"]}
|
||||||
|
},
|
||||||
|
"Array of markers to fetch. If not provided, an empty object will be returned."
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Marker", "application/json", response()),
|
||||||
|
403 => Operation.response("Error", "application/json", api_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def upsert_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Markers"],
|
||||||
|
summary: "Save position in timeline",
|
||||||
|
operationId: "MarkerController.upsert",
|
||||||
|
requestBody: Helpers.request_body("Parameters", upsert_request(), required: true),
|
||||||
|
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Marker", "application/json", response()),
|
||||||
|
403 => Operation.response("Error", "application/json", api_error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp marker do
|
||||||
|
%Schema{
|
||||||
|
title: "Marker",
|
||||||
|
description: "Schema for a marker",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
last_read_id: %Schema{type: :string},
|
||||||
|
version: %Schema{type: :integer},
|
||||||
|
updated_at: %Schema{type: :string},
|
||||||
|
pleroma: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
unread_count: %Schema{type: :integer}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"last_read_id" => "35098814",
|
||||||
|
"version" => 361,
|
||||||
|
"updated_at" => "2019-11-26T22:37:25.239Z",
|
||||||
|
"pleroma" => %{"unread_count" => 5}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp response do
|
||||||
|
%Schema{
|
||||||
|
title: "MarkersResponse",
|
||||||
|
description: "Response schema for markers",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
notifications: %Schema{allOf: [marker()], nullable: true},
|
||||||
|
home: %Schema{allOf: [marker()], nullable: true}
|
||||||
|
},
|
||||||
|
items: %Schema{type: :string},
|
||||||
|
example: %{
|
||||||
|
"notifications" => %{
|
||||||
|
"last_read_id" => "35098814",
|
||||||
|
"version" => 361,
|
||||||
|
"updated_at" => "2019-11-26T22:37:25.239Z",
|
||||||
|
"pleroma" => %{"unread_count" => 0}
|
||||||
|
},
|
||||||
|
"home" => %{
|
||||||
|
"last_read_id" => "103206604258487607",
|
||||||
|
"version" => 468,
|
||||||
|
"updated_at" => "2019-11-26T22:37:25.235Z",
|
||||||
|
"pleroma" => %{"unread_count" => 10}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp upsert_request do
|
||||||
|
%Schema{
|
||||||
|
title: "MarkersUpsertRequest",
|
||||||
|
description: "Request schema for marker upsert",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
notifications: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
last_read_id: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
home: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
last_read_id: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"home" => %{
|
||||||
|
"last_read_id" => "103194548672408537",
|
||||||
|
"version" => 462,
|
||||||
|
"updated_at" => "2019-11-24T19:39:39.337Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp api_error do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{error: %Schema{type: :string}}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,96 @@
|
||||||
|
# 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.ScheduledActivityOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scheduled Statuses"],
|
||||||
|
summary: "View scheduled statuses",
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
parameters: pagination_params(),
|
||||||
|
operationId: "ScheduledActivity.index",
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of ScheduledStatus", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: ScheduledStatus
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scheduled Statuses"],
|
||||||
|
summary: "View a single scheduled status",
|
||||||
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
|
parameters: [id_param()],
|
||||||
|
operationId: "ScheduledActivity.show",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Scheduled Status", "application/json", ScheduledStatus),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scheduled Statuses"],
|
||||||
|
summary: "Schedule a status",
|
||||||
|
operationId: "ScheduledActivity.update",
|
||||||
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody:
|
||||||
|
request_body("Parameters", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
scheduled_at: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :"date-time",
|
||||||
|
description:
|
||||||
|
"ISO 8601 Datetime at which the status will be published. Must be at least 5 minutes into the future."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Scheduled Status", "application/json", ScheduledStatus),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scheduled Statuses"],
|
||||||
|
summary: "Cancel a scheduled status",
|
||||||
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
|
parameters: [id_param()],
|
||||||
|
operationId: "ScheduledActivity.delete",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty object", "application/json", %Schema{type: :object}),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, FlakeID, "Poll ID",
|
||||||
|
example: "123",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,188 @@
|
||||||
|
# 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.SubscriptionOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.PushSubscription
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Push Subscriptions"],
|
||||||
|
summary: "Subscribe to push notifications",
|
||||||
|
description:
|
||||||
|
"Add a Web Push API subscription to receive notifications. Each access token can have one push subscription. If you create a new subscription, the old subscription is deleted.",
|
||||||
|
operationId: "SubscriptionController.create",
|
||||||
|
security: [%{"oAuth" => ["push"]}],
|
||||||
|
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Push Subscription", "application/json", PushSubscription),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Push Subscriptions"],
|
||||||
|
summary: "Get current subscription",
|
||||||
|
description: "View the PushSubscription currently associated with this access token.",
|
||||||
|
operationId: "SubscriptionController.show",
|
||||||
|
security: [%{"oAuth" => ["push"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Push Subscription", "application/json", PushSubscription),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Push Subscriptions"],
|
||||||
|
summary: "Change types of notifications",
|
||||||
|
description:
|
||||||
|
"Updates the current push subscription. Only the data part can be updated. To change fundamentals, a new subscription must be created instead.",
|
||||||
|
operationId: "SubscriptionController.update",
|
||||||
|
security: [%{"oAuth" => ["push"]}],
|
||||||
|
requestBody: Helpers.request_body("Parameters", update_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Push Subscription", "application/json", PushSubscription),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Push Subscriptions"],
|
||||||
|
summary: "Remove current subscription",
|
||||||
|
description: "Removes the current Web Push API subscription.",
|
||||||
|
operationId: "SubscriptionController.delete",
|
||||||
|
security: [%{"oAuth" => ["push"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty object", "application/json", %Schema{type: :object}),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
title: "SubscriptionCreateRequest",
|
||||||
|
description: "POST body for creating a push subscription",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
subscription: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
endpoint: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Endpoint URL that is called when a notification event occurs."
|
||||||
|
},
|
||||||
|
keys: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
p256dh: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"User agent public key. Base64 encoded string of public key of ECDH key using `prime256v1` curve."
|
||||||
|
},
|
||||||
|
auth: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Auth secret. Base64 encoded string of 16 bytes of random data."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: [:p256dh, :auth]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: [:endpoint, :keys]
|
||||||
|
},
|
||||||
|
data: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
alerts: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
follow: %Schema{type: :boolean, description: "Receive follow notifications?"},
|
||||||
|
favourite: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Receive favourite notifications?"
|
||||||
|
},
|
||||||
|
reblog: %Schema{type: :boolean, description: "Receive reblog notifications?"},
|
||||||
|
mention: %Schema{type: :boolean, description: "Receive mention notifications?"},
|
||||||
|
poll: %Schema{type: :boolean, description: "Receive poll notifications?"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: [:subscription],
|
||||||
|
example: %{
|
||||||
|
"subscription" => %{
|
||||||
|
"endpoint" => "https://example.com/example/1234",
|
||||||
|
"keys" => %{
|
||||||
|
"auth" => "8eDyX_uCN0XRhSbY5hs7Hg==",
|
||||||
|
"p256dh" =>
|
||||||
|
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"data" => %{
|
||||||
|
"alerts" => %{
|
||||||
|
"follow" => true,
|
||||||
|
"mention" => true,
|
||||||
|
"poll" => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "SubscriptionUpdateRequest",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
data: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
alerts: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
follow: %Schema{type: :boolean, description: "Receive follow notifications?"},
|
||||||
|
favourite: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Receive favourite notifications?"
|
||||||
|
},
|
||||||
|
reblog: %Schema{type: :boolean, description: "Receive reblog notifications?"},
|
||||||
|
mention: %Schema{type: :boolean, description: "Receive mention notifications?"},
|
||||||
|
poll: %Schema{type: :boolean, description: "Receive poll notifications?"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"data" => %{
|
||||||
|
"alerts" => %{
|
||||||
|
"follow" => true,
|
||||||
|
"favourite" => true,
|
||||||
|
"reblog" => true,
|
||||||
|
"mention" => true,
|
||||||
|
"poll" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,6 +17,9 @@ def init(opts), do: opts
|
||||||
def call(conn, errors) do
|
def call(conn, errors) do
|
||||||
errors =
|
errors =
|
||||||
Enum.map(errors, fn
|
Enum.map(errors, fn
|
||||||
|
%{name: nil, reason: :invalid_enum} = err ->
|
||||||
|
%OpenApiSpex.Cast.Error{err | name: err.value}
|
||||||
|
|
||||||
%{name: nil} = err ->
|
%{name: nil} = err ->
|
||||||
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
# 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.Attachment do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "Attachment",
|
||||||
|
description: "Represents a file or media attachment that can be added to a status.",
|
||||||
|
type: :object,
|
||||||
|
requried: [:id, :url, :preview_url],
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "The location of the original full-size attachment"
|
||||||
|
},
|
||||||
|
remote_url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description:
|
||||||
|
"The location of the full-size original attachment on the remote website. String (URL), or null if the attachment is local",
|
||||||
|
nullable: true
|
||||||
|
},
|
||||||
|
preview_url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "The location of a scaled-down preview of the attachment"
|
||||||
|
},
|
||||||
|
text_url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "A shorter URL for the attachment"
|
||||||
|
},
|
||||||
|
description: %Schema{
|
||||||
|
type: :string,
|
||||||
|
nullable: true,
|
||||||
|
description:
|
||||||
|
"Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load"
|
||||||
|
},
|
||||||
|
type: %Schema{
|
||||||
|
type: :string,
|
||||||
|
enum: ["image", "video", "audio", "unknown"],
|
||||||
|
description: "The type of the attachment"
|
||||||
|
},
|
||||||
|
pleroma: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
mime_type: %Schema{type: :string, description: "mime type of the attachment"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
id: "1638338801",
|
||||||
|
type: "image",
|
||||||
|
url: "someurl",
|
||||||
|
remote_url: "someurl",
|
||||||
|
preview_url: "someurl",
|
||||||
|
text_url: "someurl",
|
||||||
|
description: nil,
|
||||||
|
pleroma: %{mime_type: "image/png"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,41 @@
|
||||||
|
# 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.Conversation do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "Conversation",
|
||||||
|
description: "Represents a conversation with \"direct message\" visibility.",
|
||||||
|
type: :object,
|
||||||
|
required: [:id, :accounts, :unread],
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
accounts: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: Account,
|
||||||
|
description: "Participants in the conversation"
|
||||||
|
},
|
||||||
|
unread: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Is the conversation currently marked as unread?"
|
||||||
|
},
|
||||||
|
# last_status: Status
|
||||||
|
last_status: %Schema{
|
||||||
|
allOf: [Status],
|
||||||
|
description: "The last status in the conversation, to be used for optional display"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "418450",
|
||||||
|
"unread" => true,
|
||||||
|
"accounts" => [Account.schema().example],
|
||||||
|
"last_status" => Status.schema().example
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# 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.List do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "List",
|
||||||
|
description: "Represents a list of users",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string, description: "The internal database ID of the list"},
|
||||||
|
title: %Schema{type: :string, description: "The user-defined title of the list"}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "12249",
|
||||||
|
"title" => "Friends"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,66 @@
|
||||||
|
# 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.PushSubscription do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "PushSubscription",
|
||||||
|
description: "Response schema for a push subscription",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{
|
||||||
|
anyOf: [%Schema{type: :string}, %Schema{type: :integer}],
|
||||||
|
description: "The id of the push subscription in the database."
|
||||||
|
},
|
||||||
|
endpoint: %Schema{type: :string, description: "Where push alerts will be sent to."},
|
||||||
|
server_key: %Schema{type: :string, description: "The streaming server's VAPID key."},
|
||||||
|
alerts: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Which alerts should be delivered to the endpoint.",
|
||||||
|
properties: %{
|
||||||
|
follow: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Receive a push notification when someone has followed you?"
|
||||||
|
},
|
||||||
|
favourite: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Receive a push notification when a status you created has been favourited by someone else?"
|
||||||
|
},
|
||||||
|
reblog: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Receive a push notification when a status you created has been boosted by someone else?"
|
||||||
|
},
|
||||||
|
mention: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Receive a push notification when someone else has mentioned you in a status?"
|
||||||
|
},
|
||||||
|
poll: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description:
|
||||||
|
"Receive a push notification when a poll you voted in or created has ended? "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "328_183",
|
||||||
|
"endpoint" => "https://yourdomain.example/listener",
|
||||||
|
"alerts" => %{
|
||||||
|
"follow" => true,
|
||||||
|
"favourite" => true,
|
||||||
|
"reblog" => true,
|
||||||
|
"mention" => true,
|
||||||
|
"poll" => true
|
||||||
|
},
|
||||||
|
"server_key" =>
|
||||||
|
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M="
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -0,0 +1,54 @@
|
||||||
|
# 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.ScheduledStatus do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Attachment
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "ScheduledStatus",
|
||||||
|
description: "Represents a status that will be published at a future scheduled date.",
|
||||||
|
type: :object,
|
||||||
|
required: [:id, :scheduled_at, :params],
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
scheduled_at: %Schema{type: :string, format: :"date-time"},
|
||||||
|
media_attachments: %Schema{type: :array, items: Attachment},
|
||||||
|
params: %Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:text, :visibility],
|
||||||
|
properties: %{
|
||||||
|
text: %Schema{type: :string, nullable: true},
|
||||||
|
media_ids: %Schema{type: :array, nullable: true, items: %Schema{type: :string}},
|
||||||
|
sensitive: %Schema{type: :boolean, nullable: true},
|
||||||
|
spoiler_text: %Schema{type: :string, nullable: true},
|
||||||
|
visibility: %Schema{type: VisibilityScope, nullable: true},
|
||||||
|
scheduled_at: %Schema{type: :string, format: :"date-time", nullable: true},
|
||||||
|
poll: %Schema{type: Poll, nullable: true},
|
||||||
|
in_reply_to_id: %Schema{type: :string, nullable: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
id: "3221",
|
||||||
|
scheduled_at: "2019-12-05T12:33:01.000Z",
|
||||||
|
params: %{
|
||||||
|
text: "test content",
|
||||||
|
media_ids: nil,
|
||||||
|
sensitive: nil,
|
||||||
|
spoiler_text: nil,
|
||||||
|
visibility: nil,
|
||||||
|
scheduled_at: nil,
|
||||||
|
poll: nil,
|
||||||
|
idempotency: nil,
|
||||||
|
in_reply_to_id: nil
|
||||||
|
},
|
||||||
|
media_attachments: [Attachment.schema().example]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Attachment
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||||
|
@ -50,22 +51,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||||
language: %Schema{type: :string, nullable: true},
|
language: %Schema{type: :string, nullable: true},
|
||||||
media_attachments: %Schema{
|
media_attachments: %Schema{
|
||||||
type: :array,
|
type: :array,
|
||||||
items: %Schema{
|
items: Attachment
|
||||||
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{
|
mentions: %Schema{
|
||||||
type: :array,
|
type: :array,
|
||||||
|
@ -86,7 +72,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||||
properties: %{
|
properties: %{
|
||||||
content: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
|
content: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
|
||||||
conversation_id: %Schema{type: :integer},
|
conversation_id: %Schema{type: :integer},
|
||||||
direct_conversation_id: %Schema{type: :string, nullable: true},
|
direct_conversation_id: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
nullable: true,
|
||||||
|
description:
|
||||||
|
"The ID of the Mastodon direct message conversation the status is associated with (if any)"
|
||||||
|
},
|
||||||
emoji_reactions: %Schema{
|
emoji_reactions: %Schema{
|
||||||
type: :array,
|
type: :array,
|
||||||
items: %Schema{
|
items: %Schema{
|
||||||
|
|
|
@ -27,7 +27,7 @@ def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
with %{halted: false} = conn <-
|
with %{halted: false} = conn <-
|
||||||
Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn,
|
Pleroma.Plugs.EnsureAuthenticatedPlug.call(conn,
|
||||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
unless_func: &Pleroma.Web.FederatingPlug.federating?/1
|
||||||
) do
|
) do
|
||||||
ActivityPubController.call(conn, :user)
|
ActivityPubController.call(conn, :user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,12 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index)
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action != :index)
|
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action != :index)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ConversationOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/conversations"
|
@doc "GET /api/v1/conversations"
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
participations = Participation.for_user_with_last_activity_id(user, params)
|
participations = Participation.for_user_with_last_activity_id(user, params)
|
||||||
|
@ -26,7 +29,7 @@ def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/conversations/:id/read"
|
@doc "POST /api/v1/conversations/:id/read"
|
||||||
def mark_as_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
def mark_as_read(%{assigns: %{user: user}} = conn, %{id: participation_id}) do
|
||||||
with %Participation{} = participation <-
|
with %Participation{} = participation <-
|
||||||
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
||||||
{:ok, participation} <- Participation.mark_as_read(participation) do
|
{:ok, participation} <- Participation.mark_as_read(participation) do
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
|
defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
:skip_plug,
|
:skip_plug,
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DomainBlockOperation
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do
|
||||||
|
|
||||||
@oauth_read_actions [:show, :index]
|
@oauth_read_actions [:show, :index]
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:filters"]} when action in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["read:filters"]} when action in @oauth_read_actions)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -17,60 +18,60 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do
|
||||||
%{scopes: ["write:filters"]} when action not in @oauth_read_actions
|
%{scopes: ["write:filters"]} when action not in @oauth_read_actions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FilterOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/filters"
|
@doc "GET /api/v1/filters"
|
||||||
def index(%{assigns: %{user: user}} = conn, _) do
|
def index(%{assigns: %{user: user}} = conn, _) do
|
||||||
filters = Filter.get_filters(user)
|
filters = Filter.get_filters(user)
|
||||||
|
|
||||||
render(conn, "filters.json", filters: filters)
|
render(conn, "index.json", filters: filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/filters"
|
@doc "POST /api/v1/filters"
|
||||||
def create(
|
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||||
%{assigns: %{user: user}} = conn,
|
|
||||||
%{"phrase" => phrase, "context" => context} = params
|
|
||||||
) do
|
|
||||||
query = %Filter{
|
query = %Filter{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
phrase: phrase,
|
phrase: params.phrase,
|
||||||
context: context,
|
context: params.context,
|
||||||
hide: Map.get(params, "irreversible", false),
|
hide: params.irreversible,
|
||||||
whole_word: Map.get(params, "boolean", true)
|
whole_word: params.whole_word
|
||||||
# expires_at
|
# TODO: support `expires_in` parameter (as in Mastodon API)
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, response} = Filter.create(query)
|
{:ok, response} = Filter.create(query)
|
||||||
|
|
||||||
render(conn, "filter.json", filter: response)
|
render(conn, "show.json", filter: response)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/filters/:id"
|
@doc "GET /api/v1/filters/:id"
|
||||||
def show(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
def show(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
||||||
filter = Filter.get(filter_id, user)
|
filter = Filter.get(filter_id, user)
|
||||||
|
|
||||||
render(conn, "filter.json", filter: filter)
|
render(conn, "show.json", filter: filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PUT /api/v1/filters/:id"
|
@doc "PUT /api/v1/filters/:id"
|
||||||
def update(
|
def update(
|
||||||
%{assigns: %{user: user}} = conn,
|
%{assigns: %{user: user}, body_params: params} = conn,
|
||||||
%{"phrase" => phrase, "context" => context, "id" => filter_id} = params
|
%{id: filter_id}
|
||||||
) do
|
) do
|
||||||
query = %Filter{
|
params =
|
||||||
user_id: user.id,
|
params
|
||||||
filter_id: filter_id,
|
|> Map.delete(:irreversible)
|
||||||
phrase: phrase,
|
|> Map.put(:hide, params[:irreversible])
|
||||||
context: context,
|
|> Enum.reject(fn {_key, value} -> is_nil(value) end)
|
||||||
hide: Map.get(params, "irreversible", nil),
|
|> Map.new()
|
||||||
whole_word: Map.get(params, "boolean", true)
|
|
||||||
# expires_at
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, response} = Filter.update(query)
|
# TODO: support `expires_in` parameter (as in Mastodon API)
|
||||||
render(conn, "filter.json", filter: response)
|
|
||||||
|
with %Filter{} = filter <- Filter.get(filter_id, user),
|
||||||
|
{:ok, %Filter{} = filter} <- Filter.update(filter, params) do
|
||||||
|
render(conn, "show.json", filter: filter)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "DELETE /api/v1/filters/:id"
|
@doc "DELETE /api/v1/filters/:id"
|
||||||
def delete(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
def delete(%{assigns: %{user: user}} = conn, %{id: filter_id}) do
|
||||||
query = %Filter{
|
query = %Filter{
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: filter_id
|
filter_id: filter_id
|
||||||
|
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(:assign_follower when action != :index)
|
plug(:assign_follower when action != :index)
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
@ -21,6 +22,8 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
||||||
%{scopes: ["follow", "write:follows"]} when action != :index
|
%{scopes: ["follow", "write:follows"]} when action != :index
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FollowRequestOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/follow_requests"
|
@doc "GET /api/v1/follow_requests"
|
||||||
def index(%{assigns: %{user: followed}} = conn, _params) do
|
def index(%{assigns: %{user: followed}} = conn, _params) do
|
||||||
follow_requests = User.get_follow_requests(followed)
|
follow_requests = User.get_follow_requests(followed)
|
||||||
|
@ -42,7 +45,7 @@ def reject(%{assigns: %{user: followed, follower: follower}} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_follower(%{params: %{"id" => id}} = conn, _) do
|
defp assign_follower(%{params: %{id: id}} = conn, _) do
|
||||||
case User.get_cached_by_id(id) do
|
case User.get_cached_by_id(id) do
|
||||||
%User{} = follower -> assign(conn, :follower, follower)
|
%User{} = follower -> assign(conn, :follower, follower)
|
||||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||||
|
|
|
@ -5,12 +5,16 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
plug(OpenApiSpex.Plug.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
:skip_plug,
|
:skip_plug,
|
||||||
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||||
when action in [:show, :peers]
|
when action in [:show, :peers]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/instance"
|
@doc "GET /api/v1/instance"
|
||||||
def show(conn, _params) do
|
def show(conn, _params) do
|
||||||
render(conn, "show.json")
|
render(conn, "show.json")
|
||||||
|
|
|
@ -9,20 +9,17 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
|
||||||
plug(:list_by_id_and_user when action not in [:index, :create])
|
|
||||||
|
|
||||||
@oauth_read_actions [:index, :show, :list_accounts]
|
@oauth_read_actions [:index, :show, :list_accounts]
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(:list_by_id_and_user when action not in [:index, :create])
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write:lists"]} when action not in @oauth_read_actions)
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:lists"]}
|
|
||||||
when action not in @oauth_read_actions
|
|
||||||
)
|
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ListOperation
|
||||||
|
|
||||||
# GET /api/v1/lists
|
# GET /api/v1/lists
|
||||||
def index(%{assigns: %{user: user}} = conn, opts) do
|
def index(%{assigns: %{user: user}} = conn, opts) do
|
||||||
lists = Pleroma.List.for_user(user, opts)
|
lists = Pleroma.List.for_user(user, opts)
|
||||||
|
@ -30,7 +27,7 @@ def index(%{assigns: %{user: user}} = conn, opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /api/v1/lists
|
# POST /api/v1/lists
|
||||||
def create(%{assigns: %{user: user}} = conn, %{"title" => title}) do
|
def create(%{assigns: %{user: user}, body_params: %{title: title}} = conn, _) do
|
||||||
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
with {:ok, %Pleroma.List{} = list} <- Pleroma.List.create(title, user) do
|
||||||
render(conn, "show.json", list: list)
|
render(conn, "show.json", list: list)
|
||||||
end
|
end
|
||||||
|
@ -42,7 +39,7 @@ def show(%{assigns: %{list: list}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# PUT /api/v1/lists/:id
|
# PUT /api/v1/lists/:id
|
||||||
def update(%{assigns: %{list: list}} = conn, %{"title" => title}) do
|
def update(%{assigns: %{list: list}, body_params: %{title: title}} = conn, _) do
|
||||||
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
with {:ok, list} <- Pleroma.List.rename(list, title) do
|
||||||
render(conn, "show.json", list: list)
|
render(conn, "show.json", list: list)
|
||||||
end
|
end
|
||||||
|
@ -65,7 +62,7 @@ def list_accounts(%{assigns: %{user: user, list: list}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /api/v1/lists/:id/accounts
|
# POST /api/v1/lists/:id/accounts
|
||||||
def add_to_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
def add_to_list(%{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn, _) do
|
||||||
Enum.each(account_ids, fn account_id ->
|
Enum.each(account_ids, fn account_id ->
|
||||||
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||||
Pleroma.List.follow(list, followed)
|
Pleroma.List.follow(list, followed)
|
||||||
|
@ -76,7 +73,10 @@ def add_to_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
# DELETE /api/v1/lists/:id/accounts
|
# DELETE /api/v1/lists/:id/accounts
|
||||||
def remove_from_list(%{assigns: %{list: list}} = conn, %{"account_ids" => account_ids}) do
|
def remove_from_list(
|
||||||
|
%{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn,
|
||||||
|
_
|
||||||
|
) do
|
||||||
Enum.each(account_ids, fn account_id ->
|
Enum.each(account_ids, fn account_id ->
|
||||||
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
with %User{} = followed <- User.get_cached_by_id(account_id) do
|
||||||
Pleroma.List.unfollow(list, followed)
|
Pleroma.List.unfollow(list, followed)
|
||||||
|
@ -86,7 +86,7 @@ def remove_from_list(%{assigns: %{list: list}} = conn, %{"account_ids" => accoun
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp list_by_id_and_user(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
|
defp list_by_id_and_user(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do
|
||||||
case Pleroma.List.get(id, user) do
|
case Pleroma.List.get(id, user) do
|
||||||
%Pleroma.List{} = list -> assign(conn, :list, list)
|
%Pleroma.List{} = list -> assign(conn, :list, list)
|
||||||
nil -> conn |> render_error(:not_found, "List not found") |> halt()
|
nil -> conn |> render_error(:not_found, "List not found") |> halt()
|
||||||
|
|
|
@ -6,6 +6,8 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"]}
|
%{scopes: ["read:statuses"]}
|
||||||
|
@ -16,14 +18,18 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MarkerOperation
|
||||||
|
|
||||||
# GET /api/v1/markers
|
# GET /api/v1/markers
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
markers = Pleroma.Marker.get_markers(user, params["timeline"])
|
markers = Pleroma.Marker.get_markers(user, params[:timeline])
|
||||||
render(conn, "markers.json", %{markers: markers})
|
render(conn, "markers.json", %{markers: markers})
|
||||||
end
|
end
|
||||||
|
|
||||||
# POST /api/v1/markers
|
# POST /api/v1/markers
|
||||||
def upsert(%{assigns: %{user: user}} = conn, params) do
|
def upsert(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||||
|
params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
|
||||||
|
|
||||||
with {:ok, result} <- Pleroma.Marker.upsert(user, params),
|
with {:ok, result} <- Pleroma.Marker.upsert(user, params),
|
||||||
markers <- Map.values(result) do
|
markers <- Map.values(result) do
|
||||||
render(conn, "markers.json", %{markers: markers})
|
render(conn, "markers.json", %{markers: markers})
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||||
|
|
||||||
@oauth_read_actions [:show, :index]
|
@oauth_read_actions [:show, :index]
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.ReportController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create)
|
plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ReportOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ReportOperation
|
||||||
|
|
|
@ -11,17 +11,21 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
|
||||||
alias Pleroma.ScheduledActivity
|
alias Pleroma.ScheduledActivity
|
||||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||||
|
|
||||||
plug(:assign_scheduled_activity when action != :index)
|
|
||||||
|
|
||||||
@oauth_read_actions [:show, :index]
|
@oauth_read_actions [:show, :index]
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions)
|
||||||
|
plug(:assign_scheduled_activity when action != :index)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ScheduledActivityOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/scheduled_statuses"
|
@doc "GET /api/v1/scheduled_statuses"
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
|
||||||
|
|
||||||
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
|
with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(scheduled_activities)
|
|> add_link_headers(scheduled_activities)
|
||||||
|
@ -35,7 +39,7 @@ def show(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PUT /api/v1/scheduled_statuses/:id"
|
@doc "PUT /api/v1/scheduled_statuses/:id"
|
||||||
def update(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, params) do
|
def update(%{assigns: %{scheduled_activity: scheduled_activity}, body_params: params} = conn, _) do
|
||||||
with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
|
with {:ok, scheduled_activity} <- ScheduledActivity.update(scheduled_activity, params) do
|
||||||
render(conn, "show.json", scheduled_activity: scheduled_activity)
|
render(conn, "show.json", scheduled_activity: scheduled_activity)
|
||||||
end
|
end
|
||||||
|
@ -48,7 +52,7 @@ def delete(%{assigns: %{scheduled_activity: scheduled_activity}} = conn, _params
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{"id" => id}} = conn, _) do
|
defp assign_scheduled_activity(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do
|
||||||
case ScheduledActivity.get(user, id) do
|
case ScheduledActivity.get(user, id) do
|
||||||
%ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity)
|
%ScheduledActivity{} = activity -> assign(conn, :scheduled_activity, activity)
|
||||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||||
|
|
|
@ -11,14 +11,16 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
plug(:restrict_push_enabled)
|
||||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
||||||
|
|
||||||
plug(:restrict_push_enabled)
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.SubscriptionOperation
|
||||||
|
|
||||||
# Creates PushSubscription
|
# Creates PushSubscription
|
||||||
# POST /api/v1/push/subscription
|
# POST /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
def create(%{assigns: %{user: user, token: token}, body_params: params} = conn, _) do
|
||||||
with {:ok, _} <- Subscription.delete_if_exists(user, token),
|
with {:ok, _} <- Subscription.delete_if_exists(user, token),
|
||||||
{:ok, subscription} <- Subscription.create(user, token, params) do
|
{:ok, subscription} <- Subscription.create(user, token, params) do
|
||||||
render(conn, "show.json", subscription: subscription)
|
render(conn, "show.json", subscription: subscription)
|
||||||
|
@ -28,7 +30,7 @@ def create(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||||
# Gets PushSubscription
|
# Gets PushSubscription
|
||||||
# GET /api/v1/push/subscription
|
# GET /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
def show(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
with {:ok, subscription} <- Subscription.get(user, token) do
|
with {:ok, subscription} <- Subscription.get(user, token) do
|
||||||
render(conn, "show.json", subscription: subscription)
|
render(conn, "show.json", subscription: subscription)
|
||||||
end
|
end
|
||||||
|
@ -37,7 +39,7 @@ def get(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||||
# Updates PushSubscription
|
# Updates PushSubscription
|
||||||
# PUT /api/v1/push/subscription
|
# PUT /api/v1/push/subscription
|
||||||
#
|
#
|
||||||
def update(%{assigns: %{user: user, token: token}} = conn, params) do
|
def update(%{assigns: %{user: user, token: token}, body_params: params} = conn, _) do
|
||||||
with {:ok, subscription} <- Subscription.update(user, token, params) do
|
with {:ok, subscription} <- Subscription.update(user, token, params) do
|
||||||
render(conn, "show.json", subscription: subscription)
|
render(conn, "show.json", subscription: subscription)
|
||||||
end
|
end
|
||||||
|
@ -66,7 +68,7 @@ defp restrict_push_enabled(conn, _) do
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|> json(dgettext("errors", "Not found"))
|
|> json(%{error: dgettext("errors", "Record not found")})
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
|
|
|
@ -7,11 +7,11 @@ defmodule Pleroma.Web.MastodonAPI.FilterView do
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
|
|
||||||
def render("filters.json", %{filters: filters} = opts) do
|
def render("index.json", %{filters: filters}) do
|
||||||
render_many(filters, FilterView, "filter.json", opts)
|
render_many(filters, FilterView, "show.json")
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("filter.json", %{filter: filter}) do
|
def render("show.json", %{filter: filter}) do
|
||||||
expires_at =
|
expires_at =
|
||||||
if filter.expires_at do
|
if filter.expires_at do
|
||||||
Utils.to_masto_date(filter.expires_at)
|
Utils.to_masto_date(filter.expires_at)
|
||||||
|
|
|
@ -6,12 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.MarkerView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
def render("markers.json", %{markers: markers}) do
|
def render("markers.json", %{markers: markers}) do
|
||||||
Enum.reduce(markers, %{}, fn m, acc ->
|
Map.new(markers, fn m ->
|
||||||
Map.put_new(acc, m.timeline, %{
|
{m.timeline,
|
||||||
last_read_id: m.last_read_id,
|
%{
|
||||||
version: m.lock_version,
|
last_read_id: m.last_read_id,
|
||||||
updated_at: NaiveDateTime.to_iso8601(m.updated_at)
|
version: m.lock_version,
|
||||||
})
|
updated_at: NaiveDateTime.to_iso8601(m.updated_at)
|
||||||
|
}}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
||||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
unless_func: &Pleroma.Web.FederatingPlug.federating?/1
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
|
|
@ -25,9 +25,9 @@ defmodule Pleroma.Web.Push.Subscription do
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@supported_alert_types ~w[follow favourite mention reblog]
|
@supported_alert_types ~w[follow favourite mention reblog]a
|
||||||
|
|
||||||
defp alerts(%{"data" => %{"alerts" => alerts}}) do
|
defp alerts(%{data: %{alerts: alerts}}) do
|
||||||
alerts = Map.take(alerts, @supported_alert_types)
|
alerts = Map.take(alerts, @supported_alert_types)
|
||||||
%{"alerts" => alerts}
|
%{"alerts" => alerts}
|
||||||
end
|
end
|
||||||
|
@ -44,9 +44,9 @@ def create(
|
||||||
%User{} = user,
|
%User{} = user,
|
||||||
%Token{} = token,
|
%Token{} = token,
|
||||||
%{
|
%{
|
||||||
"subscription" => %{
|
subscription: %{
|
||||||
"endpoint" => endpoint,
|
endpoint: endpoint,
|
||||||
"keys" => %{"auth" => key_auth, "p256dh" => key_p256dh}
|
keys: %{auth: key_auth, p256dh: key_p256dh}
|
||||||
}
|
}
|
||||||
} = params
|
} = params
|
||||||
) do
|
) do
|
||||||
|
|
|
@ -188,6 +188,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
post("/reports/:id/notes", AdminAPIController, :report_notes_create)
|
||||||
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete)
|
||||||
|
|
||||||
|
get("/statuses/:id", AdminAPIController, :status_show)
|
||||||
put("/statuses/:id", AdminAPIController, :status_update)
|
put("/statuses/:id", AdminAPIController, :status_update)
|
||||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
delete("/statuses/:id", AdminAPIController, :status_delete)
|
||||||
get("/statuses", AdminAPIController, :list_statuses)
|
get("/statuses", AdminAPIController, :list_statuses)
|
||||||
|
@ -436,7 +437,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
||||||
|
|
||||||
post("/push/subscription", SubscriptionController, :create)
|
post("/push/subscription", SubscriptionController, :create)
|
||||||
get("/push/subscription", SubscriptionController, :get)
|
get("/push/subscription", SubscriptionController, :show)
|
||||||
put("/push/subscription", SubscriptionController, :update)
|
put("/push/subscription", SubscriptionController, :update)
|
||||||
delete("/push/subscription", SubscriptionController, :delete)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
||||||
plug(:assign_id)
|
plug(:assign_id)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug,
|
||||||
unless_func: &Pleroma.Web.FederatingPlug.federating?/0
|
unless_func: &Pleroma.Web.FederatingPlug.federating?/1
|
||||||
)
|
)
|
||||||
|
|
||||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||||
|
|
|
@ -200,11 +200,17 @@ def skip_plug(conn) do
|
||||||
|
|
||||||
@impl Plug
|
@impl Plug
|
||||||
@doc """
|
@doc """
|
||||||
If marked as skipped, returns `conn`, otherwise calls `perform/2`.
|
Before-plug hook that
|
||||||
|
* ensures the plug is not skipped
|
||||||
|
* processes `:if_func` / `:unless_func` functional pre-run conditions
|
||||||
|
* adds plug to the list of called plugs and calls `perform/2` if checks are passed
|
||||||
|
|
||||||
Note: multiple invocations of the same plug (with different or same options) are allowed.
|
Note: multiple invocations of the same plug (with different or same options) are allowed.
|
||||||
"""
|
"""
|
||||||
def call(%Plug.Conn{} = conn, options) do
|
def call(%Plug.Conn{} = conn, options) do
|
||||||
if PlugHelper.plug_skipped?(conn, __MODULE__) do
|
if PlugHelper.plug_skipped?(conn, __MODULE__) ||
|
||||||
|
(options[:if_func] && !options[:if_func].(conn)) ||
|
||||||
|
(options[:unless_func] && options[:unless_func].(conn)) do
|
||||||
conn
|
conn
|
||||||
else
|
else
|
||||||
conn =
|
conn =
|
||||||
|
|
|
@ -86,54 +86,24 @@ def represent_user(user, "XML") do
|
||||||
|> XmlBuilder.to_doc()
|
|> XmlBuilder.to_doc()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_magic_key("data:application/magic-public-key," <> magic_key) do
|
|
||||||
{:ok, magic_key}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_magic_key(nil) do
|
|
||||||
Logger.debug("Undefined magic key.")
|
|
||||||
{:ok, nil}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_magic_key(_) do
|
|
||||||
{:error, "Missing magic key data."}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp webfinger_from_xml(doc) do
|
defp webfinger_from_xml(doc) do
|
||||||
with magic_key <- XML.string_from_xpath(~s{//Link[@rel="magic-public-key"]/@href}, doc),
|
subject = XML.string_from_xpath("//Subject", doc)
|
||||||
{:ok, magic_key} <- get_magic_key(magic_key),
|
|
||||||
topic <-
|
|
||||||
XML.string_from_xpath(
|
|
||||||
~s{//Link[@rel="http://schemas.google.com/g/2010#updates-from"]/@href},
|
|
||||||
doc
|
|
||||||
),
|
|
||||||
subject <- XML.string_from_xpath("//Subject", doc),
|
|
||||||
subscribe_address <-
|
|
||||||
XML.string_from_xpath(
|
|
||||||
~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template},
|
|
||||||
doc
|
|
||||||
),
|
|
||||||
ap_id <-
|
|
||||||
XML.string_from_xpath(
|
|
||||||
~s{//Link[@rel="self" and @type="application/activity+json"]/@href},
|
|
||||||
doc
|
|
||||||
) do
|
|
||||||
data = %{
|
|
||||||
"magic_key" => magic_key,
|
|
||||||
"topic" => topic,
|
|
||||||
"subject" => subject,
|
|
||||||
"subscribe_address" => subscribe_address,
|
|
||||||
"ap_id" => ap_id
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, data}
|
subscribe_address =
|
||||||
else
|
~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}
|
||||||
{:error, e} ->
|
|> XML.string_from_xpath(doc)
|
||||||
{:error, e}
|
|
||||||
|
|
||||||
e ->
|
ap_id =
|
||||||
{:error, e}
|
~s{//Link[@rel="self" and @type="application/activity+json"]/@href}
|
||||||
end
|
|> XML.string_from_xpath(doc)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
"subject" => subject,
|
||||||
|
"subscribe_address" => subscribe_address,
|
||||||
|
"ap_id" => ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp webfinger_from_json(doc) do
|
defp webfinger_from_json(doc) do
|
||||||
|
@ -146,9 +116,6 @@ defp webfinger_from_json(doc) do
|
||||||
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
||||||
Map.put(data, "ap_id", link["href"])
|
Map.put(data, "ap_id", link["href"])
|
||||||
|
|
||||||
{_, "http://ostatus.org/schema/1.0/subscribe"} ->
|
|
||||||
Map.put(data, "subscribe_address", link["template"])
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Logger.debug("Unhandled type: #{inspect(link["type"])}")
|
Logger.debug("Unhandled type: #{inspect(link["type"])}")
|
||||||
data
|
data
|
||||||
|
@ -194,13 +161,15 @@ def finger(account) do
|
||||||
URI.parse(account).host
|
URI.parse(account).host
|
||||||
end
|
end
|
||||||
|
|
||||||
|
encoded_account = URI.encode("acct:#{account}")
|
||||||
|
|
||||||
address =
|
address =
|
||||||
case find_lrdd_template(domain) do
|
case find_lrdd_template(domain) do
|
||||||
{:ok, template} ->
|
{:ok, template} ->
|
||||||
String.replace(template, "{uri}", URI.encode(account))
|
String.replace(template, "{uri}", encoded_account)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
"https://#{domain}/.well-known/webfinger?resource=acct:#{account}"
|
"https://#{domain}/.well-known/webfinger?resource=#{encoded_account}"
|
||||||
end
|
end
|
||||||
|
|
||||||
with response <-
|
with response <-
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.RemoveMagicKeyField do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:magic_key, :string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -141,17 +141,15 @@ test "updating a filter" do
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
query_two = %Pleroma.Filter{
|
changes = %{
|
||||||
user_id: user.id,
|
|
||||||
filter_id: 1,
|
|
||||||
phrase: "who",
|
phrase: "who",
|
||||||
context: ["home", "timeline"]
|
context: ["home", "timeline"]
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, filter_one} = Pleroma.Filter.create(query_one)
|
{:ok, filter_one} = Pleroma.Filter.create(query_one)
|
||||||
{:ok, filter_two} = Pleroma.Filter.update(query_two)
|
{:ok, filter_two} = Pleroma.Filter.update(filter_one, changes)
|
||||||
assert filter_one != filter_two
|
assert filter_one != filter_two
|
||||||
assert filter_two.phrase == query_two.phrase
|
assert filter_two.phrase == changes.phrase
|
||||||
assert filter_two.context == query_two.context
|
assert filter_two.context == changes.context
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,8 +27,8 @@ test "it continues if a user is assigned", %{conn: conn} do
|
||||||
describe "with :if_func / :unless_func options" do
|
describe "with :if_func / :unless_func options" do
|
||||||
setup do
|
setup do
|
||||||
%{
|
%{
|
||||||
true_fn: fn -> true end,
|
true_fn: fn _conn -> true end,
|
||||||
false_fn: fn -> false end
|
false_fn: fn _conn -> false end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ defp json_response_and_validate_schema(
|
||||||
status = Plug.Conn.Status.code(status)
|
status = Plug.Conn.Status.code(status)
|
||||||
|
|
||||||
unless lookup[op_id].responses[status] do
|
unless lookup[op_id].responses[status] do
|
||||||
err = "Response schema not found for #{conn.status} #{conn.method} #{conn.request_path}"
|
err = "Response schema not found for #{status} #{conn.method} #{conn.request_path}"
|
||||||
flunk(err)
|
flunk(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,18 @@ defmacro __using__(_opts) do
|
||||||
clear_config: 2
|
clear_config: 2
|
||||||
]
|
]
|
||||||
|
|
||||||
def to_datetime(naive_datetime) do
|
def to_datetime(%NaiveDateTime{} = naive_datetime) do
|
||||||
naive_datetime
|
naive_datetime
|
||||||
|> DateTime.from_naive!("Etc/UTC")
|
|> DateTime.from_naive!("Etc/UTC")
|
||||||
|> DateTime.truncate(:second)
|
|> DateTime.truncate(:second)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_datetime(datetime) when is_binary(datetime) do
|
||||||
|
datetime
|
||||||
|
|> NaiveDateTime.from_iso8601!()
|
||||||
|
|> to_datetime()
|
||||||
|
end
|
||||||
|
|
||||||
def collect_ids(collection) do
|
def collect_ids(collection) do
|
||||||
collection
|
collection
|
||||||
|> Enum.map(& &1.id)
|
|> Enum.map(& &1.id)
|
||||||
|
|
|
@ -211,7 +211,7 @@ def get(
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://squeet.me/xrd/?uri=lain@squeet.me",
|
"https://squeet.me/xrd/?uri=acct:lain@squeet.me",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -870,7 +870,7 @@ def get(
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la",
|
"https://social.heldscal.la/.well-known/webfinger?resource=acct:shp@social.heldscal.la",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -883,7 +883,7 @@ def get(
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://social.heldscal.la/.well-known/webfinger?resource=invalid_content@social.heldscal.la",
|
"https://social.heldscal.la/.well-known/webfinger?resource=acct:invalid_content@social.heldscal.la",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -900,7 +900,7 @@ def get("http://framatube.org/.well-known/host-meta", _, _, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"http://framatube.org/main/xrd?uri=framasoft@framatube.org",
|
"http://framatube.org/main/xrd?uri=acct:framasoft@framatube.org",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -959,7 +959,7 @@ def get("http://gerzilla.de/.well-known/host-meta", _, _, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://gerzilla.de/xrd/?uri=kaniini@gerzilla.de",
|
"https://gerzilla.de/xrd/?uri=acct:kaniini@gerzilla.de",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -1155,7 +1155,7 @@ def get("http://404.site" <> _, _, _, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=lain@zetsubou.xn--q9jyb4c",
|
"https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=acct:lain@zetsubou.xn--q9jyb4c",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
@ -1168,7 +1168,7 @@ def get(
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=https://zetsubou.xn--q9jyb4c/users/lain",
|
"https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource=acct:https://zetsubou.xn--q9jyb4c/users/lain",
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
|
|
|
@ -820,21 +820,29 @@ test "it inserts an incoming sensitive activity into the database", %{
|
||||||
activity: activity
|
activity: activity
|
||||||
} do
|
} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
conn = assign(conn, :user, user)
|
||||||
object = Map.put(activity["object"], "sensitive", true)
|
object = Map.put(activity["object"], "sensitive", true)
|
||||||
activity = Map.put(activity, "object", object)
|
activity = Map.put(activity, "object", object)
|
||||||
|
|
||||||
result =
|
response =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|
||||||
|> put_req_header("content-type", "application/activity+json")
|
|> put_req_header("content-type", "application/activity+json")
|
||||||
|> post("/users/#{user.nickname}/outbox", activity)
|
|> post("/users/#{user.nickname}/outbox", activity)
|
||||||
|> json_response(201)
|
|> json_response(201)
|
||||||
|
|
||||||
assert Activity.get_by_ap_id(result["id"])
|
assert Activity.get_by_ap_id(response["id"])
|
||||||
assert result["object"]
|
assert response["object"]
|
||||||
assert %Object{data: object} = Object.normalize(result["object"])
|
assert %Object{data: response_object} = Object.normalize(response["object"])
|
||||||
assert object["sensitive"] == activity["object"]["sensitive"]
|
assert response_object["sensitive"] == true
|
||||||
assert object["content"] == activity["object"]["content"]
|
assert response_object["content"] == activity["object"]["content"]
|
||||||
|
|
||||||
|
representation =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get(response["id"])
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert representation["object"]["sensitive"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
|
test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
|
||||||
|
|
|
@ -18,9 +18,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
|
import ExUnit.CaptureLog
|
||||||
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
import Mock
|
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
@ -2403,4 +2404,51 @@ defp private_messages(_) do
|
||||||
u3: %{r1: r3_1.id, r2: r3_2.id},
|
u3: %{r1: r3_1.id, r2: r3_2.id},
|
||||||
u4: %{r1: r4_1.id}}
|
u4: %{r1: r4_1.id}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "maybe_update_follow_information/1" do
|
||||||
|
setup do
|
||||||
|
clear_config([:instance, :external_user_synchronization], true)
|
||||||
|
|
||||||
|
user = %{
|
||||||
|
local: false,
|
||||||
|
ap_id: "https://gensokyo.2hu/users/raymoo",
|
||||||
|
following_address: "https://gensokyo.2hu/users/following",
|
||||||
|
follower_address: "https://gensokyo.2hu/users/followers",
|
||||||
|
type: "Person"
|
||||||
|
}
|
||||||
|
|
||||||
|
%{user: user}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "logs an error when it can't fetch the info", %{user: user} do
|
||||||
|
assert capture_log(fn ->
|
||||||
|
ActivityPub.maybe_update_follow_information(user)
|
||||||
|
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "just returns the input if the user type is Application", %{
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
user =
|
||||||
|
user
|
||||||
|
|> Map.put(:type, "Application")
|
||||||
|
|
||||||
|
refute capture_log(fn ->
|
||||||
|
assert ^user = ActivityPub.maybe_update_follow_information(user)
|
||||||
|
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it just returns the input if the user has no following/follower addresses", %{
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
user =
|
||||||
|
user
|
||||||
|
|> Map.put(:following_address, nil)
|
||||||
|
|> Map.put(:follower_address, nil)
|
||||||
|
|
||||||
|
refute capture_log(fn ->
|
||||||
|
assert ^user = ActivityPub.maybe_update_follow_information(user)
|
||||||
|
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
@ -737,6 +738,39 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "pagination works correctly with service users", %{conn: conn} do
|
||||||
|
service1 = insert(:user, ap_id: Web.base_url() <> "/relay")
|
||||||
|
service2 = insert(:user, ap_id: Web.base_url() <> "/internal/fetch")
|
||||||
|
insert_list(25, :user)
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users1} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users1) == 10
|
||||||
|
assert service1 not in [users1]
|
||||||
|
assert service2 not in [users1]
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users2} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users2) == 10
|
||||||
|
assert service1 not in [users2]
|
||||||
|
assert service2 not in [users2]
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users3} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users3) == 6
|
||||||
|
assert service1 not in [users3]
|
||||||
|
assert service2 not in [users3]
|
||||||
|
end
|
||||||
|
|
||||||
test "renders empty array for the second page", %{conn: conn} do
|
test "renders empty array for the second page", %{conn: conn} do
|
||||||
insert(:user)
|
insert(:user)
|
||||||
|
|
||||||
|
@ -1620,6 +1654,25 @@ test "returns 403 when requested by anonymous" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/statuses/:id" do
|
||||||
|
test "not found", %{conn: conn} do
|
||||||
|
assert conn
|
||||||
|
|> get("/api/pleroma/admin/statuses/not_found")
|
||||||
|
|> json_response(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "shows activity", %{conn: conn} do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/statuses/#{activity.id}")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response["id"] == activity.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "PUT /api/pleroma/admin/statuses/:id" do
|
describe "PUT /api/pleroma/admin/statuses/:id" do
|
||||||
setup do
|
setup do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
@ -3526,7 +3579,7 @@ test "errors", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "success", %{conn: conn} do
|
test "success", %{conn: conn} do
|
||||||
base_url = Pleroma.Web.base_url()
|
base_url = Web.base_url()
|
||||||
app_name = "Trusted app"
|
app_name = "Trusted app"
|
||||||
|
|
||||||
response =
|
response =
|
||||||
|
@ -3547,7 +3600,7 @@ test "success", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with trusted", %{conn: conn} do
|
test "with trusted", %{conn: conn} do
|
||||||
base_url = Pleroma.Web.base_url()
|
base_url = Web.base_url()
|
||||||
app_name = "Trusted app"
|
app_name = "Trusted app"
|
||||||
|
|
||||||
response =
|
response =
|
||||||
|
|
|
@ -36,7 +36,7 @@ test "returns a list of conversations", %{user: user_one, conn: conn} do
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/conversations")
|
res_conn = get(conn, "/api/v1/conversations")
|
||||||
|
|
||||||
assert response = json_response(res_conn, 200)
|
assert response = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{
|
%{
|
||||||
|
@ -91,18 +91,18 @@ test "filters conversations by recipients", %{user: user_one, conn: conn} do
|
||||||
"visibility" => "direct"
|
"visibility" => "direct"
|
||||||
})
|
})
|
||||||
|
|
||||||
[conversation1, conversation2] =
|
assert [conversation1, conversation2] =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/conversations", %{"recipients" => [user_two.id]})
|
|> get("/api/v1/conversations?recipients[]=#{user_two.id}")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert conversation1["last_status"]["id"] == direct5.id
|
assert conversation1["last_status"]["id"] == direct5.id
|
||||||
assert conversation2["last_status"]["id"] == direct1.id
|
assert conversation2["last_status"]["id"] == direct1.id
|
||||||
|
|
||||||
[conversation1] =
|
[conversation1] =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/conversations", %{"recipients" => [user_two.id, user_three.id]})
|
|> get("/api/v1/conversations?recipients[]=#{user_two.id}&recipients[]=#{user_three.id}")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert conversation1["last_status"]["id"] == direct3.id
|
assert conversation1["last_status"]["id"] == direct3.id
|
||||||
end
|
end
|
||||||
|
@ -126,7 +126,7 @@ test "updates the last_status on reply", %{user: user_one, conn: conn} do
|
||||||
[%{"last_status" => res_last_status}] =
|
[%{"last_status" => res_last_status}] =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/conversations")
|
|> get("/api/v1/conversations")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert res_last_status["id"] == direct_reply.id
|
assert res_last_status["id"] == direct_reply.id
|
||||||
end
|
end
|
||||||
|
@ -154,12 +154,12 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do
|
||||||
[%{"id" => direct_conversation_id, "unread" => true}] =
|
[%{"id" => direct_conversation_id, "unread" => true}] =
|
||||||
user_two_conn
|
user_two_conn
|
||||||
|> get("/api/v1/conversations")
|
|> get("/api/v1/conversations")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
%{"unread" => false} =
|
%{"unread" => false} =
|
||||||
user_two_conn
|
user_two_conn
|
||||||
|> post("/api/v1/conversations/#{direct_conversation_id}/read")
|
|> post("/api/v1/conversations/#{direct_conversation_id}/read")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
||||||
|
@ -175,7 +175,7 @@ test "the user marks a conversation as read", %{user: user_one, conn: conn} do
|
||||||
[%{"unread" => true}] =
|
[%{"unread" => true}] =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/conversations")
|
|> get("/api/v1/conversations")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
|
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
||||||
|
|
|
@ -15,9 +15,12 @@ test "creating a filter" do
|
||||||
context: ["home"]
|
context: ["home"]
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
|
||||||
|
|
||||||
assert response = json_response(conn, 200)
|
assert response = json_response_and_validate_schema(conn, 200)
|
||||||
assert response["phrase"] == filter.phrase
|
assert response["phrase"] == filter.phrase
|
||||||
assert response["context"] == filter.context
|
assert response["context"] == filter.context
|
||||||
assert response["irreversible"] == false
|
assert response["irreversible"] == false
|
||||||
|
@ -48,12 +51,12 @@ test "fetching a list of filters" do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/filters")
|
|> get("/api/v1/filters")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert response ==
|
assert response ==
|
||||||
render_json(
|
render_json(
|
||||||
FilterView,
|
FilterView,
|
||||||
"filters.json",
|
"index.json",
|
||||||
filters: [filter_two, filter_one]
|
filters: [filter_two, filter_one]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -72,7 +75,7 @@ test "get a filter" do
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
|
conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
|
||||||
|
|
||||||
assert _response = json_response(conn, 200)
|
assert response = json_response_and_validate_schema(conn, 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update a filter" do
|
test "update a filter" do
|
||||||
|
@ -82,7 +85,8 @@ test "update a filter" do
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
filter_id: 2,
|
filter_id: 2,
|
||||||
phrase: "knight",
|
phrase: "knight",
|
||||||
context: ["home"]
|
context: ["home"],
|
||||||
|
hide: true
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, _filter} = Pleroma.Filter.create(query)
|
{:ok, _filter} = Pleroma.Filter.create(query)
|
||||||
|
@ -93,14 +97,17 @@ test "update a filter" do
|
||||||
}
|
}
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
put(conn, "/api/v1/filters/#{query.filter_id}", %{
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/filters/#{query.filter_id}", %{
|
||||||
phrase: new.phrase,
|
phrase: new.phrase,
|
||||||
context: new.context
|
context: new.context
|
||||||
})
|
})
|
||||||
|
|
||||||
assert response = json_response(conn, 200)
|
assert response = json_response_and_validate_schema(conn, 200)
|
||||||
assert response["phrase"] == new.phrase
|
assert response["phrase"] == new.phrase
|
||||||
assert response["context"] == new.context
|
assert response["context"] == new.context
|
||||||
|
assert response["irreversible"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
test "delete a filter" do
|
test "delete a filter" do
|
||||||
|
@ -117,7 +124,6 @@ test "delete a filter" do
|
||||||
|
|
||||||
conn = delete(conn, "/api/v1/filters/#{filter.filter_id}")
|
conn = delete(conn, "/api/v1/filters/#{filter.filter_id}")
|
||||||
|
|
||||||
assert response = json_response(conn, 200)
|
assert json_response_and_validate_schema(conn, 200) == %{}
|
||||||
assert response == %{}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ test "/api/v1/follow_requests works", %{user: user, conn: conn} do
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/follow_requests")
|
conn = get(conn, "/api/v1/follow_requests")
|
||||||
|
|
||||||
assert [relationship] = json_response(conn, 200)
|
assert [relationship] = json_response_and_validate_schema(conn, 200)
|
||||||
assert to_string(other_user.id) == relationship["id"]
|
assert to_string(other_user.id) == relationship["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize")
|
conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize")
|
||||||
|
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response_and_validate_schema(conn, 200)
|
||||||
assert to_string(other_user.id) == relationship["id"]
|
assert to_string(other_user.id) == relationship["id"]
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
@ -62,7 +62,7 @@ test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject")
|
conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject")
|
||||||
|
|
||||||
assert relationship = json_response(conn, 200)
|
assert relationship = json_response_and_validate_schema(conn, 200)
|
||||||
assert to_string(other_user.id) == relationship["id"]
|
assert to_string(other_user.id) == relationship["id"]
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
|
||||||
|
|
||||||
test "get instance information", %{conn: conn} do
|
test "get instance information", %{conn: conn} do
|
||||||
conn = get(conn, "/api/v1/instance")
|
conn = get(conn, "/api/v1/instance")
|
||||||
assert result = json_response(conn, 200)
|
assert result = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
email = Pleroma.Config.get([:instance, :email])
|
email = Pleroma.Config.get([:instance, :email])
|
||||||
# Note: not checking for "max_toot_chars" since it's optional
|
# Note: not checking for "max_toot_chars" since it's optional
|
||||||
|
@ -56,7 +56,7 @@ test "get instance stats", %{conn: conn} do
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/instance")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
assert result = json_response(conn, 200)
|
assert result = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
stats = result["stats"]
|
stats = result["stats"]
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ test "get peers", %{conn: conn} do
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/instance/peers")
|
conn = get(conn, "/api/v1/instance/peers")
|
||||||
|
|
||||||
assert result = json_response(conn, 200)
|
assert result = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
assert ["peer1.com", "peer2.com"] == Enum.sort(result)
|
assert ["peer1.com", "peer2.com"] == Enum.sort(result)
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,37 +12,44 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
||||||
test "creating a list" do
|
test "creating a list" do
|
||||||
%{conn: conn} = oauth_access(["write:lists"])
|
%{conn: conn} = oauth_access(["write:lists"])
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/lists", %{"title" => "cuties"})
|
assert %{"title" => "cuties"} =
|
||||||
|
conn
|
||||||
assert %{"title" => title} = json_response(conn, 200)
|
|> put_req_header("content-type", "application/json")
|
||||||
assert title == "cuties"
|
|> post("/api/v1/lists", %{"title" => "cuties"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders error for invalid params" do
|
test "renders error for invalid params" do
|
||||||
%{conn: conn} = oauth_access(["write:lists"])
|
%{conn: conn} = oauth_access(["write:lists"])
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/lists", %{"title" => nil})
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/lists", %{"title" => nil})
|
||||||
|
|
||||||
assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity)
|
assert %{"error" => "title - null value where string expected."} =
|
||||||
|
json_response_and_validate_schema(conn, 400)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "listing a user's lists" do
|
test "listing a user's lists" do
|
||||||
%{conn: conn} = oauth_access(["read:lists", "write:lists"])
|
%{conn: conn} = oauth_access(["read:lists", "write:lists"])
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/lists", %{"title" => "cuties"})
|
|> post("/api/v1/lists", %{"title" => "cuties"})
|
||||||
|> json_response(:ok)
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/lists", %{"title" => "cofe"})
|
|> post("/api/v1/lists", %{"title" => "cofe"})
|
||||||
|> json_response(:ok)
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/lists")
|
conn = get(conn, "/api/v1/lists")
|
||||||
|
|
||||||
assert [
|
assert [
|
||||||
%{"id" => _, "title" => "cofe"},
|
%{"id" => _, "title" => "cofe"},
|
||||||
%{"id" => _, "title" => "cuties"}
|
%{"id" => _, "title" => "cuties"}
|
||||||
] = json_response(conn, :ok)
|
] = json_response_and_validate_schema(conn, :ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "adding users to a list" do
|
test "adding users to a list" do
|
||||||
|
@ -50,9 +57,12 @@ test "adding users to a list" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
conn = post(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
assert %{} ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
assert %{} == json_response(conn, 200)
|
|
||||||
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
||||||
assert following == [other_user.follower_address]
|
assert following == [other_user.follower_address]
|
||||||
end
|
end
|
||||||
|
@ -65,9 +75,12 @@ test "removing users from a list" do
|
||||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||||
|
|
||||||
conn = delete(conn, "/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
assert %{} ==
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
assert %{} == json_response(conn, 200)
|
|
||||||
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
%Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
|
||||||
assert following == [third_user.follower_address]
|
assert following == [third_user.follower_address]
|
||||||
end
|
end
|
||||||
|
@ -83,7 +96,7 @@ test "listing users in a list" do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
|> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
|
||||||
|
|
||||||
assert [%{"id" => id}] = json_response(conn, 200)
|
assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
|
||||||
assert id == to_string(other_user.id)
|
assert id == to_string(other_user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,7 +109,7 @@ test "retrieving a list" do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/lists/#{list.id}")
|
|> get("/api/v1/lists/#{list.id}")
|
||||||
|
|
||||||
assert %{"id" => id} = json_response(conn, 200)
|
assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
|
||||||
assert id == to_string(list.id)
|
assert id == to_string(list.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -105,17 +118,18 @@ test "renders 404 if list is not found" do
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/lists/666")
|
conn = get(conn, "/api/v1/lists/666")
|
||||||
|
|
||||||
assert %{"error" => "List not found"} = json_response(conn, :not_found)
|
assert %{"error" => "List not found"} = json_response_and_validate_schema(conn, :not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renaming a list" do
|
test "renaming a list" do
|
||||||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||||
{:ok, list} = Pleroma.List.create("name", user)
|
{:ok, list} = Pleroma.List.create("name", user)
|
||||||
|
|
||||||
conn = put(conn, "/api/v1/lists/#{list.id}", %{"title" => "newname"})
|
assert %{"title" => "newname"} =
|
||||||
|
conn
|
||||||
assert %{"title" => name} = json_response(conn, 200)
|
|> put_req_header("content-type", "application/json")
|
||||||
assert name == "newname"
|
|> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "validates title when renaming a list" do
|
test "validates title when renaming a list" do
|
||||||
|
@ -125,9 +139,11 @@ test "validates title when renaming a list" do
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> put("/api/v1/lists/#{list.id}", %{"title" => " "})
|
|> put("/api/v1/lists/#{list.id}", %{"title" => " "})
|
||||||
|
|
||||||
assert %{"error" => "can't be blank"} == json_response(conn, :unprocessable_entity)
|
assert %{"error" => "can't be blank"} ==
|
||||||
|
json_response_and_validate_schema(conn, :unprocessable_entity)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deleting a list" do
|
test "deleting a list" do
|
||||||
|
@ -136,7 +152,7 @@ test "deleting a list" do
|
||||||
|
|
||||||
conn = delete(conn, "/api/v1/lists/#{list.id}")
|
conn = delete(conn, "/api/v1/lists/#{list.id}")
|
||||||
|
|
||||||
assert %{} = json_response(conn, 200)
|
assert %{} = json_response_and_validate_schema(conn, 200)
|
||||||
assert is_nil(Repo.get(Pleroma.List, list.id))
|
assert is_nil(Repo.get(Pleroma.List, list.id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,8 +22,8 @@ test "gets markers with correct scopes", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|
|> get("/api/v1/markers?timeline[]=notifications")
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert response == %{
|
assert response == %{
|
||||||
"notifications" => %{
|
"notifications" => %{
|
||||||
|
@ -45,7 +45,7 @@ test "gets markers with missed scopes", %{conn: conn} do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|
|> get("/api/v1/markers", %{timeline: ["notifications"]})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
|
|
||||||
assert response == %{"error" => "Insufficient permissions: read:statuses."}
|
assert response == %{"error" => "Insufficient permissions: read:statuses."}
|
||||||
end
|
end
|
||||||
|
@ -60,11 +60,12 @@ test "creates a marker with correct scopes", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/markers", %{
|
|> post("/api/v1/markers", %{
|
||||||
home: %{last_read_id: "777"},
|
home: %{last_read_id: "777"},
|
||||||
notifications: %{"last_read_id" => "69420"}
|
notifications: %{"last_read_id" => "69420"}
|
||||||
})
|
})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
"notifications" => %{
|
"notifications" => %{
|
||||||
|
@ -89,11 +90,12 @@ test "updates exist marker", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/markers", %{
|
|> post("/api/v1/markers", %{
|
||||||
home: %{last_read_id: "777"},
|
home: %{last_read_id: "777"},
|
||||||
notifications: %{"last_read_id" => "69888"}
|
notifications: %{"last_read_id" => "69888"}
|
||||||
})
|
})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert response == %{
|
assert response == %{
|
||||||
"notifications" => %{
|
"notifications" => %{
|
||||||
|
@ -112,11 +114,12 @@ test "creates a marker with missed scopes", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|> post("/api/v1/markers", %{
|
|> post("/api/v1/markers", %{
|
||||||
home: %{last_read_id: "777"},
|
home: %{last_read_id: "777"},
|
||||||
notifications: %{"last_read_id" => "69420"}
|
notifications: %{"last_read_id" => "69420"}
|
||||||
})
|
})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
|
|
||||||
assert response == %{"error" => "Insufficient permissions: write:statuses."}
|
assert response == %{"error" => "Insufficient permissions: write:statuses."}
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,19 +24,19 @@ test "shows scheduled activities" do
|
||||||
# min_id
|
# min_id
|
||||||
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
|
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
|
||||||
|
|
||||||
result = json_response(conn_res, 200)
|
result = json_response_and_validate_schema(conn_res, 200)
|
||||||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
|
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
|
||||||
|
|
||||||
# since_id
|
# since_id
|
||||||
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
|
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
|
||||||
|
|
||||||
result = json_response(conn_res, 200)
|
result = json_response_and_validate_schema(conn_res, 200)
|
||||||
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
|
assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
|
||||||
|
|
||||||
# max_id
|
# max_id
|
||||||
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
|
conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
|
||||||
|
|
||||||
result = json_response(conn_res, 200)
|
result = json_response_and_validate_schema(conn_res, 200)
|
||||||
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
|
assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,12 +46,12 @@ test "shows a scheduled activity" do
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
||||||
|
|
||||||
assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
|
assert %{"id" => scheduled_activity_id} = json_response_and_validate_schema(res_conn, 200)
|
||||||
assert scheduled_activity_id == scheduled_activity.id |> to_string()
|
assert scheduled_activity_id == scheduled_activity.id |> to_string()
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/scheduled_statuses/404")
|
res_conn = get(conn, "/api/v1/scheduled_statuses/404")
|
||||||
|
|
||||||
assert %{"error" => "Record not found"} = json_response(res_conn, 404)
|
assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "updates a scheduled activity" do
|
test "updates a scheduled activity" do
|
||||||
|
@ -74,22 +74,32 @@ test "updates a scheduled activity" do
|
||||||
assert job.args == %{"activity_id" => scheduled_activity.id}
|
assert job.args == %{"activity_id" => scheduled_activity.id}
|
||||||
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at)
|
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at)
|
||||||
|
|
||||||
new_scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 120)
|
new_scheduled_at =
|
||||||
|
NaiveDateTime.utc_now()
|
||||||
|
|> Timex.shift(minutes: 120)
|
||||||
|
|> Timex.format!("%Y-%m-%dT%H:%M:%S.%fZ", :strftime)
|
||||||
|
|
||||||
res_conn =
|
res_conn =
|
||||||
put(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
|
||||||
scheduled_at: new_scheduled_at
|
scheduled_at: new_scheduled_at
|
||||||
})
|
})
|
||||||
|
|
||||||
assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
|
assert %{"scheduled_at" => expected_scheduled_at} =
|
||||||
|
json_response_and_validate_schema(res_conn, 200)
|
||||||
|
|
||||||
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
|
assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
|
||||||
job = refresh_record(job)
|
job = refresh_record(job)
|
||||||
|
|
||||||
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at)
|
assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at)
|
||||||
|
|
||||||
res_conn = put(conn, "/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
|
res_conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
|
||||||
|
|
||||||
assert %{"error" => "Record not found"} = json_response(res_conn, 404)
|
assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "deletes a scheduled activity" do
|
test "deletes a scheduled activity" do
|
||||||
|
@ -115,7 +125,7 @@ test "deletes a scheduled activity" do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
||||||
|
|
||||||
assert %{} = json_response(res_conn, 200)
|
assert %{} = json_response_and_validate_schema(res_conn, 200)
|
||||||
refute Repo.get(ScheduledActivity, scheduled_activity.id)
|
refute Repo.get(ScheduledActivity, scheduled_activity.id)
|
||||||
refute Repo.get(Oban.Job, job.id)
|
refute Repo.get(Oban.Job, job.id)
|
||||||
|
|
||||||
|
@ -124,6 +134,6 @@ test "deletes a scheduled activity" do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
|> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
|
||||||
|
|
||||||
assert %{"error" => "Record not found"} = json_response(res_conn, 404)
|
assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Web.Push
|
alias Pleroma.Web.Push
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do
|
||||||
build_conn()
|
build_conn()
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> assign(:token, token)
|
|> assign(:token, token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|
||||||
%{conn: conn, user: user, token: token}
|
%{conn: conn, user: user, token: token}
|
||||||
end
|
end
|
||||||
|
@ -47,8 +49,8 @@ defmacro assert_error_when_disable_push(do: yield) do
|
||||||
test "returns error when push disabled ", %{conn: conn} do
|
test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> post("/api/v1/push/subscription", %{})
|
|> post("/api/v1/push/subscription", %{subscription: @sub})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -59,7 +61,7 @@ test "successful creation", %{conn: conn} do
|
||||||
"data" => %{"alerts" => %{"mention" => true, "test" => true}},
|
"data" => %{"alerts" => %{"mention" => true, "test" => true}},
|
||||||
"subscription" => @sub
|
"subscription" => @sub
|
||||||
})
|
})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
[subscription] = Pleroma.Repo.all(Subscription)
|
[subscription] = Pleroma.Repo.all(Subscription)
|
||||||
|
|
||||||
|
@ -77,7 +79,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/push/subscription", %{})
|
|> get("/api/v1/push/subscription", %{})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -85,9 +87,9 @@ test "returns error when user hasn't subscription", %{conn: conn} do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/push/subscription", %{})
|
|> get("/api/v1/push/subscription", %{})
|
||||||
|> json_response(404)
|
|> json_response_and_validate_schema(404)
|
||||||
|
|
||||||
assert "Not found" == res
|
assert %{"error" => "Record not found"} == res
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns a user subsciption", %{conn: conn, user: user, token: token} do
|
test "returns a user subsciption", %{conn: conn, user: user, token: token} do
|
||||||
|
@ -101,7 +103,7 @@ test "returns a user subsciption", %{conn: conn, user: user, token: token} do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/push/subscription", %{})
|
|> get("/api/v1/push/subscription", %{})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
expect = %{
|
expect = %{
|
||||||
"alerts" => %{"mention" => true},
|
"alerts" => %{"mention" => true},
|
||||||
|
@ -130,7 +132,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
|
|> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -140,7 +142,7 @@ test "returns updated subsciption", %{conn: conn, subscription: subscription} do
|
||||||
|> put("/api/v1/push/subscription", %{
|
|> put("/api/v1/push/subscription", %{
|
||||||
data: %{"alerts" => %{"mention" => false, "follow" => true}}
|
data: %{"alerts" => %{"mention" => false, "follow" => true}}
|
||||||
})
|
})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
expect = %{
|
expect = %{
|
||||||
"alerts" => %{"follow" => true, "mention" => false},
|
"alerts" => %{"follow" => true, "mention" => false},
|
||||||
|
@ -158,7 +160,7 @@ test "returns error when push disabled ", %{conn: conn} do
|
||||||
assert_error_when_disable_push do
|
assert_error_when_disable_push do
|
||||||
conn
|
conn
|
||||||
|> delete("/api/v1/push/subscription", %{})
|
|> delete("/api/v1/push/subscription", %{})
|
||||||
|> json_response(403)
|
|> json_response_and_validate_schema(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -166,9 +168,9 @@ test "returns error when user hasn't subscription", %{conn: conn} do
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> delete("/api/v1/push/subscription", %{})
|
|> delete("/api/v1/push/subscription", %{})
|
||||||
|> json_response(404)
|
|> json_response_and_validate_schema(404)
|
||||||
|
|
||||||
assert "Not found" == res
|
assert %{"error" => "Record not found"} == res
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns empty result and delete user subsciption", %{
|
test "returns empty result and delete user subsciption", %{
|
||||||
|
@ -186,7 +188,7 @@ test "returns empty result and delete user subsciption", %{
|
||||||
res =
|
res =
|
||||||
conn
|
conn
|
||||||
|> delete("/api/v1/push/subscription", %{})
|
|> delete("/api/v1/push/subscription", %{})
|
||||||
|> json_response(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert %{} == res
|
assert %{} == res
|
||||||
refute Pleroma.Repo.get(Subscription, subscription.id)
|
refute Pleroma.Repo.get(Subscription, subscription.id)
|
||||||
|
|
|
@ -402,11 +402,17 @@ test "attachments" do
|
||||||
pleroma: %{mime_type: "image/png"}
|
pleroma: %{mime_type: "image/png"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api_spec = Pleroma.Web.ApiSpec.spec()
|
||||||
|
|
||||||
assert expected == StatusView.render("attachment.json", %{attachment: object})
|
assert expected == StatusView.render("attachment.json", %{attachment: object})
|
||||||
|
OpenApiSpex.TestAssertions.assert_schema(expected, "Attachment", api_spec)
|
||||||
|
|
||||||
# If theres a "id", use that instead of the generated one
|
# If theres a "id", use that instead of the generated one
|
||||||
object = Map.put(object, "id", 2)
|
object = Map.put(object, "id", 2)
|
||||||
assert %{id: "2"} = StatusView.render("attachment.json", %{attachment: object})
|
result = StatusView.render("attachment.json", %{attachment: object})
|
||||||
|
|
||||||
|
assert %{id: "2"} = result
|
||||||
|
OpenApiSpex.TestAssertions.assert_schema(result, "Attachment", api_spec)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "put the url advertised in the Activity in to the url attribute" do
|
test "put the url advertised in the Activity in to the url attribute" do
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PlugTest do
|
||||||
|
@moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`"
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug
|
||||||
|
alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
|
|
||||||
|
import Mock
|
||||||
|
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
describe "when plug is skipped, " do
|
||||||
|
setup_with_mocks(
|
||||||
|
[
|
||||||
|
{ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []}
|
||||||
|
],
|
||||||
|
%{conn: conn}
|
||||||
|
) do
|
||||||
|
conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn)
|
||||||
|
%{conn: conn}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it neither adds plug to called plugs list nor calls `perform/2`, " <>
|
||||||
|
"regardless of :if_func / :unless_func options",
|
||||||
|
%{conn: conn} do
|
||||||
|
for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do
|
||||||
|
ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts)
|
||||||
|
|
||||||
|
refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_))
|
||||||
|
refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when plug is NOT skipped, " do
|
||||||
|
setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{})
|
||||||
|
|
||||||
|
assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
|
||||||
|
assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when :if_func option is given, calls the plug only if provided function evals tru-ish",
|
||||||
|
%{conn: conn} do
|
||||||
|
ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end})
|
||||||
|
|
||||||
|
refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
|
||||||
|
refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
|
||||||
|
ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end})
|
||||||
|
|
||||||
|
assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
|
||||||
|
assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "if :unless_func option is given, calls the plug only if provided function evals falsy",
|
||||||
|
%{conn: conn} do
|
||||||
|
ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end})
|
||||||
|
|
||||||
|
refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
|
||||||
|
refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
|
||||||
|
ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end})
|
||||||
|
|
||||||
|
assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
|
||||||
|
assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows a plug to be called multiple times (even if it's in called plugs list)", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1})
|
||||||
|
assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1}))
|
||||||
|
|
||||||
|
assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug)
|
||||||
|
|
||||||
|
conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2})
|
||||||
|
assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2}))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -67,7 +67,7 @@ test "it work for AP-only user" do
|
||||||
assert data["magic_key"] == nil
|
assert data["magic_key"] == nil
|
||||||
assert data["salmon"] == nil
|
assert data["salmon"] == nil
|
||||||
|
|
||||||
assert data["topic"] == "https://mstdn.jp/users/kPherox.atom"
|
assert data["topic"] == nil
|
||||||
assert data["subject"] == "acct:kPherox@mstdn.jp"
|
assert data["subject"] == "acct:kPherox@mstdn.jp"
|
||||||
assert data["ap_id"] == "https://mstdn.jp/users/kPherox"
|
assert data["ap_id"] == "https://mstdn.jp/users/kPherox"
|
||||||
assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}"
|
assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}"
|
||||||
|
|
Loading…
Reference in New Issue