Merge branch 'openapi/timelines' into 'develop'

Add OpenAPI spec for TimelineController

See merge request pleroma/pleroma!2506
This commit is contained in:
lain 2020-05-13 09:33:37 +00:00
commit 1307d6ca3c
5 changed files with 271 additions and 64 deletions

View file

@ -0,0 +1,199 @@
# 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.TimelineOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
import Pleroma.Web.ApiSpec.Helpers
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def home_operation do
%Operation{
tags: ["Timelines"],
summary: "Home timeline",
description: "View statuses from followed users",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [
local_param(),
with_muted_param(),
exclude_visibilities_param(),
reply_visibility_param(),
with_relationships_param() | pagination_params()
],
operationId: "TimelineController.home",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
}
}
end
def direct_operation do
%Operation{
tags: ["Timelines"],
summary: "Direct timeline",
description:
"View statuses with a “direct” privacy, from your account or in your notifications",
deprecated: true,
parameters: pagination_params(),
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "TimelineController.direct",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
}
}
end
def public_operation do
%Operation{
tags: ["Timelines"],
summary: "Public timeline",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [
local_param(),
only_media_param(),
with_muted_param(),
exclude_visibilities_param(),
reply_visibility_param(),
with_relationships_param() | pagination_params()
],
operationId: "TimelineController.public",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses()),
401 => Operation.response("Error", "application/json", ApiError)
}
}
end
def hashtag_operation do
%Operation{
tags: ["Timelines"],
summary: "Hashtag timeline",
description: "View public statuses containing the given hashtag",
security: [%{"oAuth" => ["read:statuses"]}],
parameters: [
Operation.parameter(
:tag,
:path,
%Schema{type: :string},
"Content of a #hashtag, not including # symbol.",
required: true
),
Operation.parameter(
:any,
:query,
%Schema{type: :array, items: %Schema{type: :string}},
"Statuses that also includes any of these tags"
),
Operation.parameter(
:all,
:query,
%Schema{type: :array, items: %Schema{type: :string}},
"Statuses that also includes all of these tags"
),
Operation.parameter(
:none,
:query,
%Schema{type: :array, items: %Schema{type: :string}},
"Statuses that do not include these tags"
),
local_param(),
only_media_param(),
with_muted_param(),
exclude_visibilities_param(),
with_relationships_param() | pagination_params()
],
operationId: "TimelineController.hashtag",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
}
}
end
def list_operation do
%Operation{
tags: ["Timelines"],
summary: "List timeline",
description: "View statuses in the given list timeline",
security: [%{"oAuth" => ["read:lists"]}],
parameters: [
Operation.parameter(
:list_id,
:path,
%Schema{type: :string},
"Local ID of the list in the database",
required: true
),
with_muted_param(),
exclude_visibilities_param(),
with_relationships_param() | pagination_params()
],
operationId: "TimelineController.list",
responses: %{
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
}
}
end
defp array_of_statuses do
%Schema{
title: "ArrayOfStatuses",
type: :array,
items: Status,
example: [Status.schema().example]
}
end
defp with_relationships_param do
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
end
defp local_param do
Operation.parameter(
:local,
:query,
%Schema{allOf: [BooleanLike], default: false},
"Show only local statuses?"
)
end
defp with_muted_param do
Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users")
end
defp exclude_visibilities_param do
Operation.parameter(
:exclude_visibilities,
:query,
%Schema{type: :array, items: VisibilityScope},
"Exclude the statuses with the given visibilities"
)
end
defp reply_visibility_param do
Operation.parameter(
:reply_visibility,
:query,
%Schema{type: :string, enum: ["following", "self"]},
"Filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you."
)
end
defp only_media_param do
Operation.parameter(
:only_media,
:query,
%Schema{allOf: [BooleanLike], default: false},
"Show only statuses with media attached?"
)
end
end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1] only: [add_link_headers: 2, add_link_headers: 3, skip_relationships?: 1]
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag]) plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag])
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it: # TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
@ -37,10 +38,13 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView) plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
# GET /api/v1/timelines/home # GET /api/v1/timelines/home
def home(%{assigns: %{user: user}} = conn, params) do def home(%{assigns: %{user: user}} = conn, params) do
params = params =
params params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", ["Create", "Announce"]) |> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("muting_user", user) |> Map.put("muting_user", user)
@ -68,6 +72,7 @@ def home(%{assigns: %{user: user}} = conn, params) do
def direct(%{assigns: %{user: user}} = conn, params) do def direct(%{assigns: %{user: user}} = conn, params) do
params = params =
params params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Create") |> Map.put("type", "Create")
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("user", user) |> Map.put("user", user)
@ -90,7 +95,9 @@ def direct(%{assigns: %{user: user}} = conn, params) do
# GET /api/v1/timelines/public # GET /api/v1/timelines/public
def public(%{assigns: %{user: user}} = conn, params) do def public(%{assigns: %{user: user}} = conn, params) do
local_only = truthy_param?(params["local"]) params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
local_only = params["local"]
cfg_key = cfg_key =
if local_only do if local_only do
@ -157,8 +164,8 @@ defp hashtag_fetching(params, user, local_only) do
# GET /api/v1/timelines/tag/:tag # GET /api/v1/timelines/tag/:tag
def hashtag(%{assigns: %{user: user}} = conn, params) do def hashtag(%{assigns: %{user: user}} = conn, params) do
local_only = truthy_param?(params["local"]) params = Map.new(params, fn {key, value} -> {to_string(key), value} end)
local_only = params["local"]
activities = hashtag_fetching(params, user, local_only) activities = hashtag_fetching(params, user, local_only)
conn conn
@ -172,10 +179,11 @@ def hashtag(%{assigns: %{user: user}} = conn, params) do
end end
# GET /api/v1/timelines/list/:list_id # GET /api/v1/timelines/list/:list_id
def list(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do def list(%{assigns: %{user: user}} = conn, %{list_id: id} = params) do
with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do
params = params =
params params
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Create") |> Map.put("type", "Create")
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("user", user) |> Map.put("user", user)

View file

@ -198,7 +198,7 @@ defp deps do
{:restarter, path: "./restarter"}, {:restarter, path: "./restarter"},
{:open_api_spex, {:open_api_spex,
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"} ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"}
] ++ oauth_deps() ] ++ oauth_deps()
end end

View file

@ -74,7 +74,7 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"}, "oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
"open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "b862ebd78de0df95875cf46feb6e9607130dc2a8", [ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"]}, "open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "f296ac0924ba3cf79c7a588c4c252889df4c2edd", [ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"]},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
"phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"}, "phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"},

View file

@ -34,7 +34,7 @@ test "does NOT render account/pleroma/relationship if this is disabled by defaul
conn conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/timelines/home") |> get("/api/v1/timelines/home")
|> json_response(200) |> json_response_and_validate_schema(200)
assert Enum.all?(response, fn n -> assert Enum.all?(response, fn n ->
get_in(n, ["account", "pleroma", "relationship"]) == %{} get_in(n, ["account", "pleroma", "relationship"]) == %{}
@ -42,7 +42,7 @@ test "does NOT render account/pleroma/relationship if this is disabled by defaul
end end
test "the home timeline", %{user: user, conn: conn} do test "the home timeline", %{user: user, conn: conn} do
uri = "/api/v1/timelines/home?with_relationships=true" uri = "/api/v1/timelines/home?with_relationships=1"
following = insert(:user, nickname: "followed") following = insert(:user, nickname: "followed")
third_user = insert(:user, nickname: "repeated") third_user = insert(:user, nickname: "repeated")
@ -53,7 +53,7 @@ test "the home timeline", %{user: user, conn: conn} do
ret_conn = get(conn, uri) ret_conn = get(conn, uri)
assert Enum.empty?(json_response(ret_conn, :ok)) assert Enum.empty?(json_response_and_validate_schema(ret_conn, :ok))
{:ok, _user} = User.follow(user, following) {:ok, _user} = User.follow(user, following)
@ -78,7 +78,7 @@ test "the home timeline", %{user: user, conn: conn} do
"pleroma" => %{"relationship" => %{"following" => true}} "pleroma" => %{"relationship" => %{"following" => true}}
} }
} }
] = json_response(ret_conn, :ok) ] = json_response_and_validate_schema(ret_conn, :ok)
{:ok, _user} = User.follow(third_user, user) {:ok, _user} = User.follow(third_user, user)
@ -104,7 +104,7 @@ test "the home timeline", %{user: user, conn: conn} do
"pleroma" => %{"relationship" => %{"following" => true}} "pleroma" => %{"relationship" => %{"following" => true}}
} }
} }
] = json_response(ret_conn, :ok) ] = json_response_and_validate_schema(ret_conn, :ok)
end end
test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do
@ -117,9 +117,9 @@ test "the home timeline when the direct messages are excluded", %{user: user, co
{:ok, private_activity} = {:ok, private_activity} =
CommonAPI.post(user, %{"status" => ".", "visibility" => "private"}) CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
conn = get(conn, "/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]}) conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct")
assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"]) assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"])
assert public_activity.id in status_ids assert public_activity.id in status_ids
assert unlisted_activity.id in status_ids assert unlisted_activity.id in status_ids
assert private_activity.id in status_ids assert private_activity.id in status_ids
@ -136,17 +136,17 @@ test "the public timeline", %{conn: conn} do
_activity = insert(:note_activity, local: false) _activity = insert(:note_activity, local: false)
conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) conn = get(conn, "/api/v1/timelines/public?local=False")
assert length(json_response(conn, :ok)) == 2 assert length(json_response_and_validate_schema(conn, :ok)) == 2
conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) conn = get(build_conn(), "/api/v1/timelines/public?local=True")
assert [%{"content" => "test"}] = json_response(conn, :ok) assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) conn = get(build_conn(), "/api/v1/timelines/public?local=1")
assert [%{"content" => "test"}] = json_response(conn, :ok) assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
end end
test "the public timeline includes only public statuses for an authenticated user" do test "the public timeline includes only public statuses for an authenticated user" do
@ -158,7 +158,7 @@ test "the public timeline includes only public statuses for an authenticated use
{:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
res_conn = get(conn, "/api/v1/timelines/public") res_conn = get(conn, "/api/v1/timelines/public")
assert length(json_response(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
end end
end end
@ -176,15 +176,15 @@ defp local_and_remote_activities do
setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
test "if user is unauthenticated", %{conn: conn} do test "if user is unauthenticated", %{conn: conn} do
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert json_response(res_conn, :unauthorized) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "authorization required for timeline view" "error" => "authorization required for timeline view"
} }
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert json_response(res_conn, :unauthorized) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "authorization required for timeline view" "error" => "authorization required for timeline view"
} }
end end
@ -192,11 +192,11 @@ test "if user is unauthenticated", %{conn: conn} do
test "if user is authenticated" do test "if user is authenticated" do
%{conn: conn} = oauth_access(["read:statuses"]) %{conn: conn} = oauth_access(["read:statuses"])
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert length(json_response(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert length(json_response(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end end
end end
@ -206,24 +206,24 @@ test "if user is authenticated" do
setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
test "if user is unauthenticated", %{conn: conn} do test "if user is unauthenticated", %{conn: conn} do
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert json_response(res_conn, :unauthorized) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "authorization required for timeline view" "error" => "authorization required for timeline view"
} }
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert length(json_response(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end end
test "if user is authenticated", %{conn: _conn} do test "if user is authenticated", %{conn: _conn} do
%{conn: conn} = oauth_access(["read:statuses"]) %{conn: conn} = oauth_access(["read:statuses"])
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert length(json_response(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert length(json_response(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end end
end end
@ -233,12 +233,12 @@ test "if user is authenticated", %{conn: _conn} do
setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
test "if user is unauthenticated", %{conn: conn} do test "if user is unauthenticated", %{conn: conn} do
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert length(json_response(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert json_response(res_conn, :unauthorized) == %{ assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "authorization required for timeline view" "error" => "authorization required for timeline view"
} }
end end
@ -246,11 +246,11 @@ test "if user is unauthenticated", %{conn: conn} do
test "if user is authenticated", %{conn: _conn} do test "if user is authenticated", %{conn: _conn} do
%{conn: conn} = oauth_access(["read:statuses"]) %{conn: conn} = oauth_access(["read:statuses"])
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "true"}) res_conn = get(conn, "/api/v1/timelines/public?local=true")
assert length(json_response(res_conn, 200)) == 1 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/timelines/public", %{"local" => "false"}) res_conn = get(conn, "/api/v1/timelines/public?local=false")
assert length(json_response(res_conn, 200)) == 2 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end end
end end
@ -281,7 +281,7 @@ test "direct timeline", %{conn: conn} do
# Only direct should be visible here # Only direct should be visible here
res_conn = get(conn_user_two, "api/v1/timelines/direct") res_conn = get(conn_user_two, "api/v1/timelines/direct")
[status] = json_response(res_conn, :ok) assert [status] = json_response_and_validate_schema(res_conn, :ok)
assert %{"visibility" => "direct"} = status assert %{"visibility" => "direct"} = status
assert status["url"] != direct.data["id"] assert status["url"] != direct.data["id"]
@ -293,14 +293,14 @@ test "direct timeline", %{conn: conn} do
|> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"]))
|> get("api/v1/timelines/direct") |> get("api/v1/timelines/direct")
[status] = json_response(res_conn, :ok) [status] = json_response_and_validate_schema(res_conn, :ok)
assert %{"visibility" => "direct"} = status assert %{"visibility" => "direct"} = status
# Both should be visible here # Both should be visible here
res_conn = get(conn_user_two, "api/v1/timelines/home") res_conn = get(conn_user_two, "api/v1/timelines/home")
[_s1, _s2] = json_response(res_conn, :ok) [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok)
# Test pagination # Test pagination
Enum.each(1..20, fn _ -> Enum.each(1..20, fn _ ->
@ -313,13 +313,14 @@ test "direct timeline", %{conn: conn} do
res_conn = get(conn_user_two, "api/v1/timelines/direct") res_conn = get(conn_user_two, "api/v1/timelines/direct")
statuses = json_response(res_conn, :ok) statuses = json_response_and_validate_schema(res_conn, :ok)
assert length(statuses) == 20 assert length(statuses) == 20
res_conn = max_id = List.last(statuses)["id"]
get(conn_user_two, "api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
[status] = json_response(res_conn, :ok) res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}")
assert [status] = json_response_and_validate_schema(res_conn, :ok)
assert status["url"] != direct.data["id"] assert status["url"] != direct.data["id"]
end end
@ -344,7 +345,7 @@ test "doesn't include DMs from blocked users" do
res_conn = get(conn, "api/v1/timelines/direct") res_conn = get(conn, "api/v1/timelines/direct")
[status] = json_response(res_conn, :ok) [status] = json_response_and_validate_schema(res_conn, :ok)
assert status["id"] == direct.id assert status["id"] == direct.id
end end
end end
@ -361,7 +362,7 @@ test "list timeline", %{user: user, conn: conn} do
conn = get(conn, "/api/v1/timelines/list/#{list.id}") conn = get(conn, "/api/v1/timelines/list/#{list.id}")
assert [%{"id" => id}] = json_response(conn, :ok) assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
assert id == to_string(activity_two.id) assert id == to_string(activity_two.id)
end end
@ -384,7 +385,7 @@ test "list timeline does not leak non-public statuses for unfollowed users", %{
conn = get(conn, "/api/v1/timelines/list/#{list.id}") conn = get(conn, "/api/v1/timelines/list/#{list.id}")
assert [%{"id" => id}] = json_response(conn, :ok) assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
assert id == to_string(activity_one.id) assert id == to_string(activity_one.id)
end end
@ -401,14 +402,14 @@ test "hashtag timeline", %{conn: conn} do
nconn = get(conn, "/api/v1/timelines/tag/2hu") nconn = get(conn, "/api/v1/timelines/tag/2hu")
assert [%{"id" => id}] = json_response(nconn, :ok) assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
assert id == to_string(activity.id) assert id == to_string(activity.id)
# works for different capitalization too # works for different capitalization too
nconn = get(conn, "/api/v1/timelines/tag/2HU") nconn = get(conn, "/api/v1/timelines/tag/2HU")
assert [%{"id" => id}] = json_response(nconn, :ok) assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
assert id == to_string(activity.id) assert id == to_string(activity.id)
end end
@ -420,22 +421,21 @@ test "multi-hashtag timeline", %{conn: conn} do
{:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
{:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]}) any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1")
[status_none, status_test1, status_test] = json_response(any_test, :ok) [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok)
assert to_string(activity_test.id) == status_test["id"] assert to_string(activity_test.id) == status_test["id"]
assert to_string(activity_test1.id) == status_test1["id"] assert to_string(activity_test1.id) == status_test1["id"]
assert to_string(activity_none.id) == status_none["id"] assert to_string(activity_none.id) == status_none["id"]
restricted_test = restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none")
get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
assert [status_test1] == json_response(restricted_test, :ok) assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok)
all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none")
assert [status_none] == json_response(all_test, :ok) assert [status_none] == json_response_and_validate_schema(all_test, :ok)
end end
end end
end end