Backend settings sync (#226)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk> Reviewed-on: #226
This commit is contained in:
parent
07295f7c8c
commit
c6e63aaf6b
12 changed files with 720 additions and 3 deletions
|
@ -4,12 +4,19 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## Unreleased
|
## 2022.10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Ability to sync frontend profiles between clients, with a name attached
|
||||||
|
- Status card generation will now use the media summary if it is available
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Emoji updated to latest 15.0 draft
|
||||||
- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts`
|
- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- OAuthPlug no longer joins with the database every call and uses the user cache
|
||||||
|
- Undo activities no longer try to look up by ID, and render correctly
|
||||||
- prevent false-errors from meilisearch
|
- prevent false-errors from meilisearch
|
||||||
|
|
||||||
## 2022.09
|
## 2022.09
|
||||||
|
|
|
@ -261,7 +261,8 @@
|
||||||
password_reset_token_validity: 60 * 60 * 24,
|
password_reset_token_validity: 60 * 60 * 24,
|
||||||
profile_directory: true,
|
profile_directory: true,
|
||||||
privileged_staff: false,
|
privileged_staff: false,
|
||||||
local_bubble: []
|
local_bubble: [],
|
||||||
|
max_frontend_settings_json_chars: 100_000
|
||||||
|
|
||||||
config :pleroma, :welcome,
|
config :pleroma, :welcome,
|
||||||
direct_message: [
|
direct_message: [
|
||||||
|
|
100
lib/pleroma/akkoma/frontend_setting_profile.ex
Normal file
100
lib/pleroma/akkoma/frontend_setting_profile.ex
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
defmodule Pleroma.Akkoma.FrontendSettingsProfile do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
schema "user_frontend_setting_profiles" do
|
||||||
|
belongs_to(:user, Pleroma.User, primary_key: true, type: FlakeId.Ecto.CompatType)
|
||||||
|
field(:frontend_name, :string, primary_key: true)
|
||||||
|
field(:profile_name, :string, primary_key: true)
|
||||||
|
field(:settings, :map)
|
||||||
|
field(:version, :integer)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(%__MODULE__{} = struct, attrs) do
|
||||||
|
struct
|
||||||
|
|> cast(attrs, [:user_id, :frontend_name, :profile_name, :settings, :version])
|
||||||
|
|> validate_required([:user_id, :frontend_name, :profile_name, :settings, :version])
|
||||||
|
|> validate_length(:frontend_name, min: 1, max: 255)
|
||||||
|
|> validate_length(:profile_name, min: 1, max: 255)
|
||||||
|
|> validate_version(struct)
|
||||||
|
|> validate_number(:version, greater_than: 0)
|
||||||
|
|> validate_settings_length(Config.get([:instance, :max_frontend_settings_json_chars]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_or_update(%User{} = user, frontend_name, profile_name, settings, version) do
|
||||||
|
struct =
|
||||||
|
case get_by_user_and_frontend_name_and_profile_name(user, frontend_name, profile_name) do
|
||||||
|
nil ->
|
||||||
|
%__MODULE__{}
|
||||||
|
|
||||||
|
%__MODULE__{} = profile ->
|
||||||
|
profile
|
||||||
|
end
|
||||||
|
|
||||||
|
struct
|
||||||
|
|> changeset(%{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: version
|
||||||
|
})
|
||||||
|
|> Repo.insert_or_update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all_by_user_and_frontend_name(%User{id: user_id}, frontend_name) do
|
||||||
|
Repo.all(
|
||||||
|
from(p in __MODULE__, where: p.user_id == ^user_id and p.frontend_name == ^frontend_name)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
%User{id: user_id},
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
) do
|
||||||
|
Repo.one(
|
||||||
|
from(p in __MODULE__,
|
||||||
|
where:
|
||||||
|
p.user_id == ^user_id and p.frontend_name == ^frontend_name and
|
||||||
|
p.profile_name == ^profile_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_profile(profile) do
|
||||||
|
Repo.delete(profile)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_settings_length(
|
||||||
|
%Ecto.Changeset{changes: %{settings: settings}} = changeset,
|
||||||
|
max_length
|
||||||
|
) do
|
||||||
|
settings_json = Jason.encode!(settings)
|
||||||
|
|
||||||
|
if String.length(settings_json) > max_length do
|
||||||
|
add_error(changeset, :settings, "is too long")
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_version(changeset, %{version: nil}), do: changeset
|
||||||
|
|
||||||
|
defp validate_version(%Ecto.Changeset{changes: %{version: version}} = changeset, %{
|
||||||
|
version: prev_version
|
||||||
|
}) do
|
||||||
|
if version != prev_version + 1 do
|
||||||
|
add_error(changeset, :version, "must be incremented by 1")
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -165,6 +165,8 @@ defmodule Pleroma.User do
|
||||||
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
|
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
|
||||||
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
|
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
|
||||||
|
|
||||||
|
has_many(:frontend_profiles, Pleroma.Akkoma.FrontendSettingsProfile)
|
||||||
|
|
||||||
for {relationship_type,
|
for {relationship_type,
|
||||||
[
|
[
|
||||||
{outgoing_relation, outgoing_relation_target},
|
{outgoing_relation, outgoing_relation_target},
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{@unauthenticated_access | scopes: ["read:accounts"]}
|
||||||
|
when action in [
|
||||||
|
:list_profiles,
|
||||||
|
:get_profile
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{@unauthenticated_access | scopes: ["write:accounts"]}
|
||||||
|
when action in [
|
||||||
|
:update_profile,
|
||||||
|
:delete_profile
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FrontendSettingsOperation
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
@doc "GET /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def get_profile(conn, %{frontend_name: frontend_name, profile_name: profile_name}) do
|
||||||
|
with %FrontendSettingsProfile{} = profile <-
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
) do
|
||||||
|
conn
|
||||||
|
|> json(%{
|
||||||
|
settings: profile.settings,
|
||||||
|
version: profile.version
|
||||||
|
})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/akkoma/frontend_settings/:frontend_name"
|
||||||
|
def list_profiles(conn, %{frontend_name: frontend_name}) do
|
||||||
|
with profiles <-
|
||||||
|
FrontendSettingsProfile.get_all_by_user_and_frontend_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name
|
||||||
|
),
|
||||||
|
data <-
|
||||||
|
Enum.map(profiles, fn profile ->
|
||||||
|
%{name: profile.profile_name, version: profile.version}
|
||||||
|
end) do
|
||||||
|
json(conn, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "DELETE /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def delete_profile(conn, %{frontend_name: frontend_name, profile_name: profile_name}) do
|
||||||
|
with %FrontendSettingsProfile{} = profile <-
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
),
|
||||||
|
{:ok, _} <- FrontendSettingsProfile.delete_profile(profile) do
|
||||||
|
json(conn, %{deleted: "ok"})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "PUT /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def update_profile(%{body_params: %{settings: settings, version: version}} = conn, %{
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name
|
||||||
|
}) do
|
||||||
|
with {:ok, profile} <-
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
version
|
||||||
|
) do
|
||||||
|
conn
|
||||||
|
|> json(profile.settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,133 @@
|
||||||
|
defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
@spec open_api_operation(atom) :: Operation.t()
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec list_profiles_operation() :: Operation.t()
|
||||||
|
def list_profiles_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Retrieve frontend setting profiles"],
|
||||||
|
summary: "Frontend Settings Profiles",
|
||||||
|
description: "List frontend setting profiles",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.list_profiles",
|
||||||
|
parameters: [frontend_name_param()],
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Profiles", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
version: %Schema{type: :integer}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_profile_operation() :: Operation.t()
|
||||||
|
def get_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Retrieve frontend setting profile"],
|
||||||
|
summary: "Frontend Settings Profile",
|
||||||
|
description: "Get frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.get_profile",
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Profile", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
"version" => %Schema{type: :integer},
|
||||||
|
"settings" => %Schema{type: :object, additionalProperties: true}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
404 => Operation.response("Not Found", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec delete_profile_operation() :: Operation.t()
|
||||||
|
def delete_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Delete frontend setting profile"],
|
||||||
|
summary: "Delete frontend Settings Profile",
|
||||||
|
description: "Delete frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.delete_profile",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty", "application/json", %Schema{type: :object}),
|
||||||
|
404 => Operation.response("Not Found", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_profile_operation() :: Operation.t()
|
||||||
|
def update_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Update frontend setting profile"],
|
||||||
|
summary: "Frontend Settings Profile",
|
||||||
|
description: "Update frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
requestBody: profile_body_param(),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Settings", "application/json", %Schema{type: :object}),
|
||||||
|
422 => Operation.response("Invalid", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def frontend_name_param do
|
||||||
|
Operation.parameter(:frontend_name, :path, :string, "Frontend name",
|
||||||
|
example: "pleroma-fe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def profile_name_param do
|
||||||
|
Operation.parameter(:profile_name, :path, :string, "Profile name",
|
||||||
|
example: "mobile",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def profile_body_param do
|
||||||
|
request_body(
|
||||||
|
"Settings",
|
||||||
|
%Schema{
|
||||||
|
title: "Frontend Setting Profile",
|
||||||
|
type: :object,
|
||||||
|
required: [:version, :settings],
|
||||||
|
properties: %{
|
||||||
|
version: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Version of the profile, must increment by 1 each time",
|
||||||
|
example: 1
|
||||||
|
},
|
||||||
|
settings: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Settings of the profile",
|
||||||
|
example: %{
|
||||||
|
theme: "dark",
|
||||||
|
locale: "en"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -466,6 +466,26 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
|
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
get("/translation/languages", TranslationController, :languages)
|
get("/translation/languages", TranslationController, :languages)
|
||||||
|
|
||||||
|
get("/frontend_settings/:frontend_name", FrontendSettingsController, :list_profiles)
|
||||||
|
|
||||||
|
get(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:get_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:update_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:delete_profile
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :pleroma,
|
app: :pleroma,
|
||||||
version: version("3.2.0"),
|
version: version("3.3.0"),
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUserFrontendProfiles do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
create_if_not_exists table("user_frontend_setting_profiles", primary_key: false) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all), primary_key: true)
|
||||||
|
add(:frontend_name, :string, primary_key: true)
|
||||||
|
add(:profile_name, :string, primary_key: true)
|
||||||
|
add(:version, :integer)
|
||||||
|
add(:settings, :map)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:user_frontend_setting_profiles, [:user_id, :frontend_name]))
|
||||||
|
|
||||||
|
create_if_not_exists(
|
||||||
|
unique_index(:user_frontend_setting_profiles, [:user_id, :frontend_name, :profile_name])
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
drop_if_exists(table("user_frontend_setting_profiles"))
|
||||||
|
drop_if_exists(index(:user_frontend_setting_profiles, [:user_id, :frontend_name]))
|
||||||
|
|
||||||
|
drop_if_exists(
|
||||||
|
unique_index(:user_frontend_setting_profiles, [:user_id, :frontend_name, :profile_name])
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
196
test/pleroma/akkoma/frontend_setting_profile_test.exs
Normal file
196
test/pleroma/akkoma/frontend_setting_profile_test.exs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
defmodule Pleroma.Akkoma.FrontendSettingsProfileTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "changeset/2" do
|
||||||
|
test "valid" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: true} = FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when settings is too long" do
|
||||||
|
clear_config([:instance, :max_frontend_settings_json_chars], 10)
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"verylong" => "verylongoops"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [settings: {"is too long", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when frontend name is too short" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = ""
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [frontend_name: {"can't be blank", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when profile name is too short" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = ""
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [profile_name: {"can't be blank", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when version is negative" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [version: {"must be greater than %{number}", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "create_or_update/2" do
|
||||||
|
test "it should create a new record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
|
||||||
|
assert {:ok, %FrontendSettingsProfile{}} =
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it should update a record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
settings = %{"test" => "test2"}
|
||||||
|
|
||||||
|
assert {:ok, %FrontendSettingsProfile{settings: ^settings}} =
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_all_by_user_and_frontend_name/2" do
|
||||||
|
test "it should return all records" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: "profileA",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: "profileB",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
assert [%FrontendSettingsProfile{profile_name: "profileA"}, %{profile_name: "profileB"}] =
|
||||||
|
FrontendSettingsProfile.get_all_by_user_and_frontend_name(user, frontend_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_by_user_and_frontend_name_and_profile_name/3" do
|
||||||
|
test "it should return a record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "profileA"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %FrontendSettingsProfile{profile_name: "profileA"} =
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,122 @@
|
||||||
|
defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
describe "GET /api/v1/akkoma/frontend_settings/:frontend_name" do
|
||||||
|
test "it returns a list of profiles" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile, user: user, frontend_name: "test", profile_name: "test1")
|
||||||
|
insert(:frontend_setting_profile, user: user, frontend_name: "test", profile_name: "test2")
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/test")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == [
|
||||||
|
%{"name" => "test1", "version" => 1},
|
||||||
|
%{"name" => "test2", "version" => 1}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "it returns 404 if not found" do
|
||||||
|
%{conn: conn} = oauth_access(["read"])
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/unknown_frontend/unknown_profile")
|
||||||
|
|> json_response_and_validate_schema(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 200 if found" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"}
|
||||||
|
)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/test/test1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == %{"settings" => %{"test" => "test"}, "version" => 1}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "puts a config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
settings = %{"test" => "test2"}
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/akkoma/frontend_settings/test/test1", %{
|
||||||
|
"settings" => settings,
|
||||||
|
"version" => 1
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == settings
|
||||||
|
|
||||||
|
assert %FrontendSettingsProfile{settings: ^settings} =
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
"test",
|
||||||
|
"test1"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "refuses to overwrite a newer config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/akkoma/frontend_settings/test/test1", %{
|
||||||
|
"settings" => %{"test" => "test2"},
|
||||||
|
"version" => 1
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(422)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "deletes a config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/akkoma/frontend_settings/test/test1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
"test",
|
||||||
|
"test1"
|
||||||
|
) == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -663,4 +663,15 @@ def announcement_factory(params \\ %{}) do
|
||||||
|> Map.merge(params)
|
|> Map.merge(params)
|
||||||
|> Pleroma.Announcement.add_rendered_properties()
|
|> Pleroma.Announcement.add_rendered_properties()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def frontend_setting_profile_factory(params \\ %{}) do
|
||||||
|
%Pleroma.Akkoma.FrontendSettingsProfile{
|
||||||
|
user: build(:user),
|
||||||
|
frontend_name: "akkoma-fe",
|
||||||
|
profile_name: "default",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|> Map.merge(params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue