Add OpenAPI spec for AdminAPI.InviteTokenController

This commit is contained in:
Egor Kislitsyn 2020-05-26 15:02:51 +04:00
parent 95ebfb9190
commit 2a4f965191
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
3 changed files with 179 additions and 171 deletions

View file

@ -14,6 +14,7 @@ defmodule Pleroma.Web.AdminAPI.InviteTokenController do
require Logger require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index) plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :index)
plug( plug(
@ -23,6 +24,8 @@ defmodule Pleroma.Web.AdminAPI.InviteTokenController do
action_fallback(Pleroma.Web.AdminAPI.FallbackController) action_fallback(Pleroma.Web.AdminAPI.FallbackController)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.InviteTokenOperation
@doc "Get list of created invites" @doc "Get list of created invites"
def index(conn, _params) do def index(conn, _params) do
invites = UserInviteToken.list_invites() invites = UserInviteToken.list_invites()
@ -33,26 +36,14 @@ def index(conn, _params) do
end end
@doc "Create an account registration invite token" @doc "Create an account registration invite token"
def create(conn, params) do def create(%{body_params: params} = conn, _) do
opts = %{} {:ok, invite} = UserInviteToken.create_invite(params)
opts =
if params["max_use"],
do: Map.put(opts, :max_use, params["max_use"]),
else: opts
opts =
if params["expires_at"],
do: Map.put(opts, :expires_at, params["expires_at"]),
else: opts
{:ok, invite} = UserInviteToken.create_invite(opts)
json(conn, AccountView.render("invite.json", %{invite: invite})) json(conn, AccountView.render("invite.json", %{invite: invite}))
end end
@doc "Revokes invite by token" @doc "Revokes invite by token"
def revoke(conn, %{"token" => token}) do def revoke(%{body_params: %{token: token}} = conn, _) do
with {:ok, invite} <- UserInviteToken.find_by_token(token), with {:ok, invite} <- UserInviteToken.find_by_token(token),
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
conn conn
@ -64,7 +55,7 @@ def revoke(conn, %{"token" => token}) do
end end
@doc "Sends registration invite via email" @doc "Sends registration invite via email"
def email(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])}, with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])}, {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
{:ok, invite_token} <- UserInviteToken.create_invite(), {:ok, invite_token} <- UserInviteToken.create_invite(),
@ -73,7 +64,7 @@ def email(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
user, user,
invite_token, invite_token,
email, email,
params["name"] params[:name]
), ),
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
json_response(conn, :no_content, "") json_response(conn, :no_content, "")

View file

@ -5,14 +5,9 @@
defmodule Pleroma.Web.ApiSpec.Admin.InviteTokenOperation do defmodule Pleroma.Web.ApiSpec.Admin.InviteTokenOperation do
alias OpenApiSpex.Operation alias OpenApiSpex.Operation
alias OpenApiSpex.Schema alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
import Pleroma.Web.ApiSpec.Helpers import Pleroma.Web.ApiSpec.Helpers
import Pleroma.Web.ApiSpec.StatusOperation, only: [id_param: 0]
def open_api_operation(action) do def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation") operation = String.to_existing_atom("#{action}_operation")
@ -21,144 +16,132 @@ def open_api_operation(action) do
def index_operation do def index_operation do
%Operation{ %Operation{
tags: ["Admin", "Statuses"], tags: ["Admin", "Invites"],
operationId: "AdminAPI.StatusController.index", summary: "Get a list of generated invites",
security: [%{"oAuth" => ["read:statuses"]}], operationId: "AdminAPI.InviteTokenController.index",
parameters: [ security: [%{"oAuth" => ["read:invites"]}],
Operation.parameter(
:godmode,
:query,
%Schema{type: :boolean, default: false},
"Allows to see private statuses"
),
Operation.parameter(
:local_only,
:query,
%Schema{type: :boolean, default: false},
"Excludes remote statuses"
),
Operation.parameter(
:with_reblogs,
:query,
%Schema{type: :boolean, default: false},
"Allows to see reblogs"
),
Operation.parameter(
:page,
:query,
%Schema{type: :integer, default: 1},
"Page"
),
Operation.parameter(
:page_size,
:query,
%Schema{type: :integer, default: 50},
"Number of statuses to return"
)
],
responses: %{ responses: %{
200 => 200 =>
Operation.response("Array of statuses", "application/json", %Schema{ Operation.response("Intites", "application/json", %Schema{
type: :array, type: :object,
items: status() properties: %{
invites: %Schema{type: :array, items: invite()}
},
example: %{
"invites" => [
%{
"id" => 123,
"token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
"used" => true,
"expires_at" => nil,
"uses" => 0,
"max_use" => nil,
"invite_type" => "one_time"
}
]
}
}) })
} }
} }
end end
def show_operation do def create_operation do
%Operation{ %Operation{
tags: ["Admin", "Statuses"], tags: ["Admin", "Invites"],
summary: "Show Status", summary: "Create an account registration invite token",
operationId: "AdminAPI.StatusController.show", operationId: "AdminAPI.InviteTokenController.create",
parameters: [id_param()], security: [%{"oAuth" => ["write:invites"]}],
security: [%{"oAuth" => ["read:statuses"]}], requestBody:
responses: %{ request_body("Parameters", %Schema{
200 => Operation.response("Status", "application/json", Status),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def update_operation do
%Operation{
tags: ["Admin", "Statuses"],
summary: "Change the scope of an individual reported status",
operationId: "AdminAPI.StatusController.update",
parameters: [id_param()],
security: [%{"oAuth" => ["write:statuses"]}],
requestBody: request_body("Parameters", update_request(), required: true),
responses: %{
200 => Operation.response("Status", "application/json", Status),
400 => Operation.response("Error", "application/json", ApiError)
}
}
end
def delete_operation do
%Operation{
tags: ["Admin", "Statuses"],
summary: "Delete an individual reported status",
operationId: "AdminAPI.StatusController.delete",
parameters: [id_param()],
security: [%{"oAuth" => ["write:statuses"]}],
responses: %{
200 => empty_object_response(),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
defp status do
%Schema{
anyOf: [
Status,
%Schema{
type: :object, type: :object,
properties: %{ properties: %{
account: %Schema{allOf: [Account, admin_account()]} max_use: %Schema{type: :integer},
expires_at: %Schema{type: :string, format: :date, example: "2020-04-20"}
} }
}),
responses: %{
200 => Operation.response("Invite", "application/json", invite())
}
}
end
def revoke_operation do
%Operation{
tags: ["Admin", "Invites"],
summary: "Revoke invite by token",
operationId: "AdminAPI.InviteTokenController.revoke",
security: [%{"oAuth" => ["write:invites"]}],
requestBody:
request_body(
"Parameters",
%Schema{
type: :object,
required: [:token],
properties: %{
token: %Schema{type: :string}
}
},
required: true
),
responses: %{
200 => Operation.response("Invite", "application/json", invite()),
400 => Operation.response("Bad Request", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def email_operation do
%Operation{
tags: ["Admin", "Invites"],
summary: "Sends registration invite via email",
operationId: "AdminAPI.InviteTokenController.email",
security: [%{"oAuth" => ["write:invites"]}],
requestBody:
request_body(
"Parameters",
%Schema{
type: :object,
required: [:email],
properties: %{
email: %Schema{type: :string, format: :email},
name: %Schema{type: :string}
}
},
required: true
),
responses: %{
204 => no_content_response(),
400 => Operation.response("Bad Request", "application/json", ApiError),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
defp invite do
%Schema{
title: "Invite",
type: :object,
properties: %{
id: %Schema{type: :integer},
token: %Schema{type: :string},
used: %Schema{type: :boolean},
expires_at: %Schema{type: :string, format: :date, nullable: true},
uses: %Schema{type: :integer},
max_use: %Schema{type: :integer, nullable: true},
invite_type: %Schema{
type: :string,
enum: ["one_time", "reusable", "date_limited", "reusable_date_limited"]
} }
]
}
end
defp admin_account do
%Schema{
type: :object,
properties: %{
id: FlakeID,
avatar: %Schema{type: :string},
nickname: %Schema{type: :string},
display_name: %Schema{type: :string},
deactivated: %Schema{type: :boolean},
local: %Schema{type: :boolean},
roles: %Schema{
type: :object,
properties: %{
admin: %Schema{type: :boolean},
moderator: %Schema{type: :boolean}
}
},
tags: %Schema{type: :string},
confirmation_pending: %Schema{type: :string}
}
}
end
defp update_request do
%Schema{
type: :object,
properties: %{
sensitive: %Schema{
type: :boolean,
description: "Mark status and attached media as sensitive?"
},
visibility: VisibilityScope
}, },
example: %{ example: %{
"visibility" => "private", "id" => 123,
"sensitive" => "false" "token" => "kSQtDj_GNy2NZsL9AQDFIsHN5qdbguB6qRg3WHw6K1U=",
"used" => true,
"expires_at" => nil,
"uses" => 0,
"max_use" => nil,
"invite_type" => "one_time"
} }
} }
end end

View file

@ -32,12 +32,14 @@ test "sends invitation and returns 204", %{admin: admin, conn: conn} do
recipient_name = "J. D." recipient_name = "J. D."
conn = conn =
post( conn
conn, |> put_req_header("content-type", "application/json;charset=utf-8")
"/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}" |> post("/api/pleroma/admin/users/email_invite", %{
) email: recipient_email,
name: recipient_name
})
assert json_response(conn, :no_content) assert json_response_and_validate_schema(conn, :no_content)
token_record = List.last(Repo.all(Pleroma.UserInviteToken)) token_record = List.last(Repo.all(Pleroma.UserInviteToken))
assert token_record assert token_record
@ -69,7 +71,11 @@ test "it returns 403 if requested by a non-admin" do
build_conn() build_conn()
|> assign(:user, non_admin_user) |> assign(:user, non_admin_user)
|> assign(:token, token) |> assign(:token, token)
|> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") |> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{
email: "foo@bar.com",
name: "JD"
})
assert json_response(conn, :forbidden) assert json_response(conn, :forbidden)
end end
@ -80,7 +86,7 @@ test "email with +", %{conn: conn, admin: admin} do
conn conn
|> put_req_header("content-type", "application/json;charset=utf-8") |> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
|> json_response(:no_content) |> json_response_and_validate_schema(:no_content)
token_record = token_record =
Pleroma.UserInviteToken Pleroma.UserInviteToken
@ -116,9 +122,15 @@ test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
Config.put([:instance, :registrations_open], false) Config.put([:instance, :registrations_open], false)
Config.put([:instance, :invites_enabled], false) Config.put([:instance, :invites_enabled], false)
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/email_invite", %{
email: "foo@bar.com",
name: "JD"
})
assert json_response(conn, :bad_request) == assert json_response_and_validate_schema(conn, :bad_request) ==
%{ %{
"error" => "error" =>
"To send invites you need to set the `invites_enabled` option to true." "To send invites you need to set the `invites_enabled` option to true."
@ -129,9 +141,15 @@ test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
Config.put([:instance, :registrations_open], true) Config.put([:instance, :registrations_open], true)
Config.put([:instance, :invites_enabled], true) Config.put([:instance, :invites_enabled], true)
conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD") conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/email_invite", %{
email: "foo@bar.com",
name: "JD"
})
assert json_response(conn, :bad_request) == assert json_response_and_validate_schema(conn, :bad_request) ==
%{ %{
"error" => "error" =>
"To send invites you need to set the `registrations_open` option to false." "To send invites you need to set the `registrations_open` option to false."
@ -141,9 +159,12 @@ test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
describe "POST /api/pleroma/admin/users/invite_token" do describe "POST /api/pleroma/admin/users/invite_token" do
test "without options", %{conn: conn} do test "without options", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/users/invite_token") conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token")
invite_json = json_response(conn, 200) invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"]) invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used refute invite.used
refute invite.expires_at refute invite.expires_at
@ -153,11 +174,13 @@ test "without options", %{conn: conn} do
test "with expires_at", %{conn: conn} do test "with expires_at", %{conn: conn} do
conn = conn =
post(conn, "/api/pleroma/admin/users/invite_token", %{ conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token", %{
"expires_at" => Date.to_string(Date.utc_today()) "expires_at" => Date.to_string(Date.utc_today())
}) })
invite_json = json_response(conn, 200) invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"]) invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used refute invite.used
@ -167,9 +190,12 @@ test "with expires_at", %{conn: conn} do
end end
test "with max_use", %{conn: conn} do test "with max_use", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150}) conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
invite_json = json_response(conn, 200) invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"]) invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used refute invite.used
refute invite.expires_at refute invite.expires_at
@ -179,12 +205,14 @@ test "with max_use", %{conn: conn} do
test "with max use and expires_at", %{conn: conn} do test "with max use and expires_at", %{conn: conn} do
conn = conn =
post(conn, "/api/pleroma/admin/users/invite_token", %{ conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token", %{
"max_use" => 150, "max_use" => 150,
"expires_at" => Date.to_string(Date.utc_today()) "expires_at" => Date.to_string(Date.utc_today())
}) })
invite_json = json_response(conn, 200) invite_json = json_response_and_validate_schema(conn, 200)
invite = UserInviteToken.find_by_token!(invite_json["token"]) invite = UserInviteToken.find_by_token!(invite_json["token"])
refute invite.used refute invite.used
assert invite.expires_at == Date.utc_today() assert invite.expires_at == Date.utc_today()
@ -197,7 +225,7 @@ test "with max use and expires_at", %{conn: conn} do
test "no invites", %{conn: conn} do test "no invites", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites") conn = get(conn, "/api/pleroma/admin/users/invites")
assert json_response(conn, 200) == %{"invites" => []} assert json_response_and_validate_schema(conn, 200) == %{"invites" => []}
end end
test "with invite", %{conn: conn} do test "with invite", %{conn: conn} do
@ -205,7 +233,7 @@ test "with invite", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites") conn = get(conn, "/api/pleroma/admin/users/invites")
assert json_response(conn, 200) == %{ assert json_response_and_validate_schema(conn, 200) == %{
"invites" => [ "invites" => [
%{ %{
"expires_at" => nil, "expires_at" => nil,
@ -225,9 +253,12 @@ test "with invite", %{conn: conn} do
test "with token", %{conn: conn} do test "with token", %{conn: conn} do
{:ok, invite} = UserInviteToken.create_invite() {:ok, invite} = UserInviteToken.create_invite()
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
assert json_response(conn, 200) == %{ assert json_response_and_validate_schema(conn, 200) == %{
"expires_at" => nil, "expires_at" => nil,
"id" => invite.id, "id" => invite.id,
"invite_type" => "one_time", "invite_type" => "one_time",
@ -239,9 +270,12 @@ test "with token", %{conn: conn} do
end end
test "with invalid token", %{conn: conn} do test "with invalid token", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
assert json_response(conn, :not_found) == %{"error" => "Not found"} assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end end
end end
end end