Merge branch 'from/upstream-develop/tusooa/sync-settings' into 'develop'
Synchronized settings for apps (frontends) See merge request pleroma/pleroma!3698
This commit is contained in:
commit
93f12c0d0d
@ -725,3 +725,42 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
|||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params: none
|
* Params: none
|
||||||
* Response: HTTP 200 on success, 500 on error
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/settings/:app`
|
||||||
|
### Gets settings for some application
|
||||||
|
* Method `GET`
|
||||||
|
* Authentication: `read:accounts`
|
||||||
|
|
||||||
|
* Response: JSON. The settings for that application, or empty object if there is none.
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"some key": "some value"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updates settings for some application
|
||||||
|
* Method `PATCH`
|
||||||
|
* Authentication: `write:accounts`
|
||||||
|
* Request body: JSON object. The object will be merged recursively with old settings. If some field is set to null, it is removed.
|
||||||
|
* Example request:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"some key": "some value",
|
||||||
|
"key to remove": null,
|
||||||
|
"nested field": {
|
||||||
|
"some key": "some value",
|
||||||
|
"key to remove": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* Response: JSON. Updated (merged) settings for that application.
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"some key": "some value",
|
||||||
|
"nested field": {
|
||||||
|
"some key": "some value",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaSettingsOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
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: ["Settings"],
|
||||||
|
summary: "Get settings for an application",
|
||||||
|
description: "Get synchronized settings for an application",
|
||||||
|
operationId: "SettingsController.show",
|
||||||
|
parameters: [app_name_param()],
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("object", "application/json", object())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Settings"],
|
||||||
|
summary: "Update settings for an application",
|
||||||
|
description: "Update synchronized settings for an application",
|
||||||
|
operationId: "SettingsController.update",
|
||||||
|
parameters: [app_name_param()],
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("object", "application/json", object())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def app_name_param do
|
||||||
|
Operation.parameter(:app, :path, %Schema{type: :string}, "Application name",
|
||||||
|
example: "pleroma-fe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def object do
|
||||||
|
%Schema{
|
||||||
|
title: "Settings object",
|
||||||
|
description: "The object that contains settings for the application.",
|
||||||
|
type: :object
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "SettingsUpdateRequest",
|
||||||
|
type: :object,
|
||||||
|
description:
|
||||||
|
"The settings object to be merged with the current settings. To remove a field, set it to null.",
|
||||||
|
example: %{
|
||||||
|
"config1" => true,
|
||||||
|
"config2_to_unset" => nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,79 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.SettingsController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:accounts"]} when action in [:update]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:accounts"]} when action in [:show]
|
||||||
|
)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaSettingsOperation
|
||||||
|
|
||||||
|
@doc "GET /api/v1/pleroma/settings/:app"
|
||||||
|
def show(%{assigns: %{user: user}} = conn, %{app: app} = _params) do
|
||||||
|
conn
|
||||||
|
|> json(get_settings(user, app))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "PATCH /api/v1/pleroma/settings/:app"
|
||||||
|
def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{app: app} = _params) do
|
||||||
|
settings =
|
||||||
|
get_settings(user, app)
|
||||||
|
|> merge_recursively(body_params)
|
||||||
|
|
||||||
|
with changeset <-
|
||||||
|
Pleroma.User.update_changeset(
|
||||||
|
user,
|
||||||
|
%{pleroma_settings_store: %{app => settings}}
|
||||||
|
),
|
||||||
|
{:ok, _} <- Pleroma.Repo.update(changeset) do
|
||||||
|
conn
|
||||||
|
|> json(settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp merge_recursively(old, %{} = new) do
|
||||||
|
old = ensure_object(old)
|
||||||
|
|
||||||
|
Enum.reduce(
|
||||||
|
new,
|
||||||
|
old,
|
||||||
|
fn
|
||||||
|
{k, nil}, acc ->
|
||||||
|
Map.drop(acc, [k])
|
||||||
|
|
||||||
|
{k, %{} = new_child}, acc ->
|
||||||
|
Map.put(acc, k, merge_recursively(acc[k], new_child))
|
||||||
|
|
||||||
|
{k, v}, acc ->
|
||||||
|
Map.put(acc, k, v)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_settings(user, app) do
|
||||||
|
user.pleroma_settings_store
|
||||||
|
|> Map.get(app, %{})
|
||||||
|
|> ensure_object()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ensure_object(%{} = object) do
|
||||||
|
object
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ensure_object(_) do
|
||||||
|
%{}
|
||||||
|
end
|
||||||
|
end
|
@ -463,6 +463,13 @@ defmodule Pleroma.Web.Router do
|
|||||||
get("/birthdays", AccountController, :birthdays)
|
get("/birthdays", AccountController, :birthdays)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope [] do
|
||||||
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
get("/settings/:app", SettingsController, :show)
|
||||||
|
patch("/settings/:app", SettingsController, :update)
|
||||||
|
end
|
||||||
|
|
||||||
post("/accounts/confirmation_resend", AccountController, :confirmation_resend)
|
post("/accounts/confirmation_resend", AccountController, :confirmation_resend)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.SettingsControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "GET /api/v1/pleroma/settings/:app" do
|
||||||
|
setup do
|
||||||
|
oauth_access(["read:accounts"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it gets empty settings", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/settings/pleroma-fe")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it gets settings", %{conn: conn, user: user} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(
|
||||||
|
:user,
|
||||||
|
struct(user,
|
||||||
|
pleroma_settings_store: %{
|
||||||
|
"pleroma-fe" => %{
|
||||||
|
"foo" => "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> get("/api/v1/pleroma/settings/pleroma-fe")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert %{"foo" => "bar"} == response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/v1/pleroma/settings/:app" do
|
||||||
|
setup do
|
||||||
|
settings = %{
|
||||||
|
"foo" => "bar",
|
||||||
|
"nested" => %{
|
||||||
|
"1" => "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(
|
||||||
|
:user,
|
||||||
|
%{
|
||||||
|
pleroma_settings_store: %{
|
||||||
|
"pleroma-fe" => settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
%{conn: conn} = oauth_access(["write:accounts"], user: user)
|
||||||
|
|
||||||
|
%{conn: conn, user: user, settings: settings}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it adds keys", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/v1/pleroma/settings/pleroma-fe", %{
|
||||||
|
"foo" => "edited",
|
||||||
|
"bar" => "new",
|
||||||
|
"nested" => %{"3" => "4"}
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"foo" => "edited",
|
||||||
|
"bar" => "new",
|
||||||
|
"nested" => %{
|
||||||
|
"1" => "2",
|
||||||
|
"3" => "4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it removes keys", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/v1/pleroma/settings/pleroma-fe", %{
|
||||||
|
"foo" => nil,
|
||||||
|
"bar" => nil,
|
||||||
|
"nested" => %{
|
||||||
|
"1" => nil,
|
||||||
|
"3" => nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"nested" => %{}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it does not override settings for other apps", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user,
|
||||||
|
settings: settings
|
||||||
|
} do
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> patch("/api/v1/pleroma/settings/admin-fe", %{"foo" => "bar"})
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
user = Pleroma.User.get_by_id(user.id)
|
||||||
|
|
||||||
|
assert user.pleroma_settings_store == %{
|
||||||
|
"pleroma-fe" => settings,
|
||||||
|
"admin-fe" => %{"foo" => "bar"}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user