Add OpenApi spec to AdminAPI.ConfigController

This commit is contained in:
Egor Kislitsyn 2020-05-28 23:11:12 +04:00
parent d1ee3527ef
commit 06f20e9181
No known key found for this signature in database
GPG key ID: 1B49CB15B71E7805
3 changed files with 259 additions and 68 deletions

View file

@ -11,23 +11,26 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
@descriptions Pleroma.Docs.JSON.compile()
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
plug(
OAuthScopesPlug,
%{scopes: ["read"], admin: true}
when action in [:show, :descriptions]
)
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
def descriptions(conn, _params) do
descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
json(conn, descriptions)
end
def show(conn, %{"only_db" => true}) do
def show(conn, %{only_db: true}) do
with :ok <- configurable_from_database() do
configs = Pleroma.Repo.all(ConfigDB)
render(conn, "index.json", %{configs: configs})
@ -73,16 +76,16 @@ def show(conn, _params) do
end
end
def update(conn, %{"configs" => configs}) do
def update(%{body_params: %{configs: configs}} = conn, _) do
with :ok <- configurable_from_database() do
results =
configs
|> Enum.filter(&whitelisted_config?/1)
|> Enum.map(fn
%{"group" => group, "key" => key, "delete" => true} = params ->
ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
%{group: group, key: key, delete: true} = params ->
ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]})
%{"group" => group, "key" => key, "value" => value} ->
%{group: group, key: key, value: value} ->
ConfigDB.update_or_create(%{group: group, key: key, value: value})
end)
|> Enum.reject(fn {result, _} -> result == :error end)
@ -140,11 +143,11 @@ defp whitelisted_config?(group, key) do
end
end
defp whitelisted_config?(%{"group" => group, "key" => key}) do
defp whitelisted_config?(%{group: group, key: key}) do
whitelisted_config?(group, key)
end
defp whitelisted_config?(%{:group => group} = config) do
defp whitelisted_config?(%{group: group} = config) do
whitelisted_config?(group, config[:key])
end
end

View file

@ -0,0 +1,142 @@
# 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.Admin.ConfigOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.ApiError
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: ["Admin", "Config"],
summary: "Get list of merged default settings with saved in database",
operationId: "AdminAPI.ConfigController.show",
parameters: [
Operation.parameter(
:only_db,
:query,
%Schema{type: :boolean, default: false},
"Get only saved in database settings"
)
],
security: [%{"oAuth" => ["read"]}],
responses: %{
200 => Operation.response("Config", "application/json", config_response()),
400 => Operation.response("Bad Request", "application/json", ApiError)
}
}
end
def update_operation do
%Operation{
tags: ["Admin", "Config"],
summary: "Update config settings",
operationId: "AdminAPI.ConfigController.update",
security: [%{"oAuth" => ["write"]}],
requestBody:
request_body("Parameters", %Schema{
type: :object,
properties: %{
configs: %Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
group: %Schema{type: :string},
key: %Schema{type: :string},
value: any(),
delete: %Schema{type: :boolean},
subkeys: %Schema{type: :array, items: %Schema{type: :string}}
}
}
}
}
}),
responses: %{
200 => Operation.response("Config", "application/json", config_response()),
400 => Operation.response("Bad Request", "application/json", ApiError)
}
}
end
def descriptions_operation do
%Operation{
tags: ["Admin", "Config"],
summary: "Get JSON with config descriptions.",
operationId: "AdminAPI.ConfigController.descriptions",
security: [%{"oAuth" => ["read"]}],
responses: %{
200 =>
Operation.response("Config Descriptions", "application/json", %Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
group: %Schema{type: :string},
key: %Schema{type: :string},
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
description: %Schema{type: :string},
children: %Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
key: %Schema{type: :string},
type: %Schema{oneOf: [%Schema{type: :string}, %Schema{type: :array}]},
description: %Schema{type: :string},
suggestions: %Schema{type: :array}
}
}
}
}
}
}),
400 => Operation.response("Bad Request", "application/json", ApiError)
}
}
end
defp any do
%Schema{
oneOf: [
%Schema{type: :array},
%Schema{type: :object},
%Schema{type: :string},
%Schema{type: :integer},
%Schema{type: :boolean}
]
}
end
defp config_response do
%Schema{
type: :object,
properties: %{
configs: %Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
group: %Schema{type: :string},
key: %Schema{type: :string},
value: any()
}
}
},
need_reboot: %Schema{
type: :boolean,
description:
"If `need_reboot` is `true`, instance must be restarted, so reboot time settings can take effect"
}
}
}
end
end

View file

@ -30,7 +30,7 @@ test "when configuration from database is off", %{conn: conn} do
Config.put(:configurable_from_database, false)
conn = get(conn, "/api/pleroma/admin/config")
assert json_response(conn, 400) ==
assert json_response_and_validate_schema(conn, 400) ==
%{
"error" => "To use this endpoint you need to enable configuration from database."
}
@ -40,7 +40,7 @@ test "with settings only in db", %{conn: conn} do
config1 = insert(:config)
config2 = insert(:config)
conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
conn = get(conn, "/api/pleroma/admin/config?only_db=true")
%{
"configs" => [
@ -55,7 +55,7 @@ test "with settings only in db", %{conn: conn} do
"value" => _
}
]
} = json_response(conn, 200)
} = json_response_and_validate_schema(conn, 200)
assert key1 == config1.key
assert key2 == config2.key
@ -67,7 +67,7 @@ test "db is added to settings that are in db", %{conn: conn} do
%{"configs" => configs} =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
[instance_config] =
Enum.filter(configs, fn %{"group" => group, "key" => key} ->
@ -89,7 +89,7 @@ test "merged default setting with db settings", %{conn: conn} do
%{"configs" => configs} =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
assert length(configs) > 3
@ -133,7 +133,7 @@ test "subkeys with full update right merge", %{conn: conn} do
%{"configs" => configs} =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
vals =
Enum.filter(configs, fn %{"group" => group, "key" => key} ->
@ -152,9 +152,12 @@ test "subkeys with full update right merge", %{conn: conn} do
end
test "POST /api/pleroma/admin/config error", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{"configs" => []})
assert json_response(conn, 400) ==
assert json_response_and_validate_schema(conn, 400) ==
%{"error" => "To use this endpoint you need to enable configuration from database."}
end
@ -185,7 +188,9 @@ test "create new config setting in db", %{conn: conn} do
on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: ":pleroma", key: ":key1", value: "value1"},
%{
@ -225,7 +230,7 @@ test "create new config setting in db", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -310,7 +315,9 @@ test "save configs setting without explicit key", %{conn: conn} do
end)
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":quack",
@ -330,7 +337,7 @@ test "save configs setting without explicit key", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":quack",
@ -362,13 +369,15 @@ test "saving config with partial update", %{conn: conn} do
config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -388,8 +397,9 @@ test "saving config which need pleroma reboot", %{conn: conn} do
chat = Config.get(:chat)
on_exit(fn -> Config.put(:chat, chat) end)
assert post(
conn,
assert conn
|> put_req_header("content-type", "application/json")
|> post(
"/api/pleroma/admin/config",
%{
configs: [
@ -397,7 +407,7 @@ test "saving config which need pleroma reboot", %{conn: conn} do
]
}
)
|> json_response(200) == %{
|> json_response_and_validate_schema(200) == %{
"configs" => [
%{
"db" => [":enabled"],
@ -412,18 +422,19 @@ test "saving config which need pleroma reboot", %{conn: conn} do
configs =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
assert configs["need_reboot"]
capture_log(fn ->
assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
%{}
end) =~ "pleroma restarted"
configs =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
assert configs["need_reboot"] == false
end
@ -432,8 +443,9 @@ test "update setting which need reboot, don't change reboot flag until reboot",
chat = Config.get(:chat)
on_exit(fn -> Config.put(:chat, chat) end)
assert post(
conn,
assert conn
|> put_req_header("content-type", "application/json")
|> post(
"/api/pleroma/admin/config",
%{
configs: [
@ -441,7 +453,7 @@ test "update setting which need reboot, don't change reboot flag until reboot",
]
}
)
|> json_response(200) == %{
|> json_response_and_validate_schema(200) == %{
"configs" => [
%{
"db" => [":enabled"],
@ -453,12 +465,14 @@ test "update setting which need reboot, don't change reboot flag until reboot",
"need_reboot" => true
}
assert post(conn, "/api/pleroma/admin/config", %{
assert conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
]
})
|> json_response(200) == %{
|> json_response_and_validate_schema(200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -473,13 +487,14 @@ test "update setting which need reboot, don't change reboot flag until reboot",
}
capture_log(fn ->
assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
%{}
end) =~ "pleroma restarted"
configs =
conn
|> get("/api/pleroma/admin/config")
|> json_response(200)
|> json_response_and_validate_schema(200)
assert configs["need_reboot"] == false
end
@ -489,7 +504,9 @@ test "saving config with nested merge", %{conn: conn} do
insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: config.group,
@ -510,7 +527,7 @@ test "saving config with nested merge", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -537,7 +554,9 @@ test "saving config with nested merge", %{conn: conn} do
test "saving special atoms", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
"configs" => [
%{
"group" => ":pleroma",
@ -554,7 +573,7 @@ test "saving special atoms", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -593,7 +612,9 @@ test "saving full setting if value is in full_key_update list", %{conn: conn} do
assert Application.get_env(:logger, :backends) == []
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: config.group,
@ -603,7 +624,7 @@ test "saving full setting if value is in full_key_update list", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":logger",
@ -630,13 +651,15 @@ test "saving full setting if value is not keyword", %{conn: conn} do
)
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":tesla",
@ -664,14 +687,16 @@ test "update config setting & delete with fallback to default value", %{
)
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: config1.group, key: config1.key, value: "another_value"},
%{group: config2.group, key: config2.key, value: "another_value"}
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -696,6 +721,7 @@ test "update config setting & delete with fallback to default value", %{
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: config2.group, key: config2.key, delete: true},
@ -707,7 +733,7 @@ test "update config setting & delete with fallback to default value", %{
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => []
}
@ -717,7 +743,9 @@ test "update config setting & delete with fallback to default value", %{
test "common config example", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
"group" => ":pleroma",
@ -741,7 +769,7 @@ test "common config example", %{conn: conn} do
assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -779,7 +807,9 @@ test "common config example", %{conn: conn} do
test "tuples with more than two values", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
"group" => ":pleroma",
@ -843,7 +873,7 @@ test "tuples with more than two values", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -911,7 +941,9 @@ test "tuples with more than two values", %{conn: conn} do
test "settings with nesting map", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
"group" => ":pleroma",
@ -940,7 +972,7 @@ test "settings with nesting map", %{conn: conn} do
]
})
assert json_response(conn, 200) ==
assert json_response_and_validate_schema(conn, 200) ==
%{
"configs" => [
%{
@ -974,7 +1006,9 @@ test "settings with nesting map", %{conn: conn} do
test "value as map", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
"group" => ":pleroma",
@ -984,7 +1018,7 @@ test "value as map", %{conn: conn} do
]
})
assert json_response(conn, 200) ==
assert json_response_and_validate_schema(conn, 200) ==
%{
"configs" => [
%{
@ -999,7 +1033,9 @@ test "value as map", %{conn: conn} do
test "queues key as atom", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
"group" => ":oban",
@ -1017,7 +1053,7 @@ test "queues key as atom", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":oban",
@ -1053,7 +1089,9 @@ test "delete part of settings by atom subkeys", %{conn: conn} do
)
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: config.group,
@ -1064,7 +1102,7 @@ test "delete part of settings by atom subkeys", %{conn: conn} do
]
})
assert json_response(conn, 200) == %{
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":pleroma",
@ -1078,7 +1116,9 @@ test "delete part of settings by atom subkeys", %{conn: conn} do
test "proxy tuple localhost", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":pleroma",
@ -1099,7 +1139,7 @@ test "proxy tuple localhost", %{conn: conn} do
"db" => db
}
]
} = json_response(conn, 200)
} = json_response_and_validate_schema(conn, 200)
assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
assert ":proxy_url" in db
@ -1107,7 +1147,9 @@ test "proxy tuple localhost", %{conn: conn} do
test "proxy tuple domain", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":pleroma",
@ -1128,7 +1170,7 @@ test "proxy tuple domain", %{conn: conn} do
"db" => db
}
]
} = json_response(conn, 200)
} = json_response_and_validate_schema(conn, 200)
assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
assert ":proxy_url" in db
@ -1136,7 +1178,9 @@ test "proxy tuple domain", %{conn: conn} do
test "proxy tuple ip", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":pleroma",
@ -1157,7 +1201,7 @@ test "proxy tuple ip", %{conn: conn} do
"db" => db
}
]
} = json_response(conn, 200)
} = json_response_and_validate_schema(conn, 200)
assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
assert ":proxy_url" in db
@ -1172,7 +1216,9 @@ test "doesn't set keys not in the whitelist", %{conn: conn} do
{:not_real}
])
post(conn, "/api/pleroma/admin/config", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/config", %{
configs: [
%{group: ":pleroma", key: ":key1", value: "value1"},
%{group: ":pleroma", key: ":key2", value: "value2"},
@ -1200,7 +1246,7 @@ test "structure", %{conn: conn} do
assign(conn, :user, admin)
|> get("/api/pleroma/admin/config/descriptions")
assert [child | _others] = json_response(conn, 200)
assert [child | _others] = json_response_and_validate_schema(conn, 200)
assert child["children"]
assert child["key"]
@ -1222,7 +1268,7 @@ test "filters by database configuration whitelist", %{conn: conn} do
assign(conn, :user, admin)
|> get("/api/pleroma/admin/config/descriptions")
children = json_response(conn, 200)
children = json_response_and_validate_schema(conn, 200)
assert length(children) == 4