Merge branch 'openapi/polls' into 'develop'
Add OpenAPI spec for PollController See merge request pleroma/pleroma!2476
This commit is contained in:
commit
4c92dfb73e
4 changed files with 162 additions and 22 deletions
76
lib/pleroma/web/api_spec/operations/poll_operation.ex
Normal file
76
lib/pleroma/web/api_spec/operations/poll_operation.ex
Normal file
|
@ -0,0 +1,76 @@
|
|||
# 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.PollOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["Polls"],
|
||||
summary: "View a poll",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [id_param()],
|
||||
operationId: "PollController.show",
|
||||
responses: %{
|
||||
200 => Operation.response("Poll", "application/json", Poll),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def vote_operation do
|
||||
%Operation{
|
||||
tags: ["Polls"],
|
||||
summary: "Vote on a poll",
|
||||
parameters: [id_param()],
|
||||
operationId: "PollController.vote",
|
||||
requestBody: vote_request(),
|
||||
security: [%{"oAuth" => ["write:statuses"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Poll", "application/json", Poll),
|
||||
422 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp id_param do
|
||||
Operation.parameter(:id, :path, FlakeID, "Poll ID",
|
||||
example: "123",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
|
||||
defp vote_request do
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
choices: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :integer},
|
||||
description: "Array of own votes containing index for each option (starting from 0)"
|
||||
}
|
||||
},
|
||||
required: [:choices]
|
||||
},
|
||||
required: true,
|
||||
example: %{
|
||||
"choices" => [0, 1, 2]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
|
@ -11,26 +11,72 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
|||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Poll",
|
||||
description: "Response schema for account custom fields",
|
||||
description: "Represents a poll attached to a status",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: FlakeID,
|
||||
expires_at: %Schema{type: :string, format: "date-time"},
|
||||
expired: %Schema{type: :boolean},
|
||||
multiple: %Schema{type: :boolean},
|
||||
votes_count: %Schema{type: :integer},
|
||||
voted: %Schema{type: :boolean},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
expires_at: %Schema{
|
||||
type: :string,
|
||||
format: :"date-time",
|
||||
nullable: true,
|
||||
description: "When the poll ends"
|
||||
},
|
||||
expired: %Schema{type: :boolean, description: "Is the poll currently expired?"},
|
||||
multiple: %Schema{
|
||||
type: :boolean,
|
||||
description: "Does the poll allow multiple-choice answers?"
|
||||
},
|
||||
votes_count: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "How many votes have been received. Number, or null if `multiple` is false."
|
||||
},
|
||||
voted: %Schema{
|
||||
type: :boolean,
|
||||
nullable: true,
|
||||
description:
|
||||
"When called with a user token, has the authorized user voted? Boolean, or null if no current user."
|
||||
},
|
||||
emojis: %Schema{
|
||||
type: :array,
|
||||
items: Emoji,
|
||||
description: "Custom emoji to be used for rendering poll options."
|
||||
},
|
||||
options: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
title: "PollOption",
|
||||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string},
|
||||
votes_count: %Schema{type: :integer}
|
||||
}
|
||||
},
|
||||
description: "Possible answers for the poll."
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
id: "34830",
|
||||
expires_at: "2019-12-05T04:05:08.302Z",
|
||||
expired: true,
|
||||
multiple: false,
|
||||
votes_count: 10,
|
||||
voters_count: nil,
|
||||
voted: true,
|
||||
own_votes: [
|
||||
1
|
||||
],
|
||||
options: [
|
||||
%{
|
||||
title: "accept",
|
||||
votes_count: 6
|
||||
},
|
||||
%{
|
||||
title: "deny",
|
||||
votes_count: 4
|
||||
}
|
||||
],
|
||||
emojis: []
|
||||
}
|
||||
})
|
||||
end
|
||||
|
|
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :show
|
||||
|
@ -22,8 +24,10 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
|
@ -35,7 +39,7 @@ def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|||
end
|
||||
|
||||
@doc "POST /api/v1/polls/:id/votes"
|
||||
def vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
|
||||
def vote(%{assigns: %{user: user}, body_params: %{choices: choices}} = conn, %{id: id}) do
|
||||
with %Object{data: %{"type" => "Question"}} = object <- Object.get_by_id(id),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user),
|
||||
|
|
|
@ -24,7 +24,7 @@ test "returns poll entity for object id", %{user: user, conn: conn} do
|
|||
|
||||
conn = get(conn, "/api/v1/polls/#{object.id}")
|
||||
|
||||
response = json_response(conn, 200)
|
||||
response = json_response_and_validate_schema(conn, 200)
|
||||
id = to_string(object.id)
|
||||
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
|
||||
end
|
||||
|
@ -43,7 +43,7 @@ test "does not expose polls for private statuses", %{conn: conn} do
|
|||
|
||||
conn = get(conn, "/api/v1/polls/#{object.id}")
|
||||
|
||||
assert json_response(conn, 404)
|
||||
assert json_response_and_validate_schema(conn, 404)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,9 +65,12 @@ test "votes are added to the poll", %{conn: conn} do
|
|||
|
||||
object = Object.normalize(activity)
|
||||
|
||||
conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
|
||||
|
||||
assert json_response(conn, 200)
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
object = Object.get_by_id(object.id)
|
||||
|
||||
assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
|
||||
|
@ -85,8 +88,9 @@ test "author can't vote", %{user: user, conn: conn} do
|
|||
object = Object.normalize(activity)
|
||||
|
||||
assert conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
|
||||
|> json_response(422) == %{"error" => "Poll's author can't vote"}
|
||||
|> json_response_and_validate_schema(422) == %{"error" => "Poll's author can't vote"}
|
||||
|
||||
object = Object.get_by_id(object.id)
|
||||
|
||||
|
@ -105,8 +109,9 @@ test "does not allow multiple choices on a single-choice question", %{conn: conn
|
|||
object = Object.normalize(activity)
|
||||
|
||||
assert conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
|
||||
|> json_response(422) == %{"error" => "Too many choices"}
|
||||
|> json_response_and_validate_schema(422) == %{"error" => "Too many choices"}
|
||||
|
||||
object = Object.get_by_id(object.id)
|
||||
|
||||
|
@ -126,15 +131,21 @@ test "does not allow choice index to be greater than options count", %{conn: con
|
|||
|
||||
object = Object.normalize(activity)
|
||||
|
||||
conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
|
||||
|
||||
assert json_response(conn, 422) == %{"error" => "Invalid indices"}
|
||||
assert json_response_and_validate_schema(conn, 422) == %{"error" => "Invalid indices"}
|
||||
end
|
||||
|
||||
test "returns 404 error when object is not exist", %{conn: conn} do
|
||||
conn = post(conn, "/api/v1/polls/1/votes", %{"choices" => [0]})
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/1/votes", %{"choices" => [0]})
|
||||
|
||||
assert json_response(conn, 404) == %{"error" => "Record not found"}
|
||||
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
|
||||
end
|
||||
|
||||
test "returns 404 when poll is private and not available for user", %{conn: conn} do
|
||||
|
@ -149,9 +160,12 @@ test "returns 404 when poll is private and not available for user", %{conn: conn
|
|||
|
||||
object = Object.normalize(activity)
|
||||
|
||||
conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
|
||||
|
||||
assert json_response(conn, 404) == %{"error" => "Record not found"}
|
||||
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue