Ensure version increments

This commit is contained in:
FloatingGhost 2022-09-15 11:22:33 +01:00
parent affd255daf
commit 56e3d11807
5 changed files with 92 additions and 35 deletions

View file

@ -13,21 +13,22 @@ defmodule Pleroma.Akkoma.FrontendSettingProfile do
field(:frontend_name, :string, primary_key: true)
field(:profile_name, :string, primary_key: true)
field(:settings, :map)
field(:version, :integer, default: 1)
field(:version, :integer)
timestamps()
end
def changeset(%__MODULE__{} = struct, attrs) do
struct
|> cast(attrs, [:user_id, :frontend_name, :profile_name, :settings])
|> validate_required([:user_id, :frontend_name, :profile_name, :settings])
|> 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()
|> 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) do
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 ->
@ -42,7 +43,8 @@ def create_or_update(%User{} = user, frontend_name, profile_name, settings) do
user_id: user.id,
frontend_name: frontend_name,
profile_name: profile_name,
settings: settings
settings: settings,
version: version
})
|> Repo.insert_or_update()
end
@ -80,12 +82,14 @@ defp validate_settings_length(
end
end
defp validate_version(%Ecto.Changeset{changes: %{version: version}} = changeset) do
IO.inspect(changeset)
if version < 1 do
add_error(changeset, :version, "must be greater than 0")
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

View file

@ -44,8 +44,8 @@ def list_profiles(conn, %{frontend_name: frontend_name}) do
end
@doc "PUT /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
def update_profile(conn, %{frontend_name: frontend_name, profile_name: profile_name, settings: settings}) do
with {:ok, profile} <- FrontendSettingProfile.create_or_update(conn.assigns.user, frontend_name, profile_name, settings) do
def update_profile(%{body_params: %{settings: settings, version: version}} = conn, %{frontend_name: frontend_name, profile_name: profile_name}) do
with {:ok, profile} <- FrontendSettingProfile.create_or_update(conn.assigns.user, frontend_name, profile_name, settings, version) do
conn
|> json(profile.settings)
end

View file

@ -1,6 +1,7 @@
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
@ -51,10 +52,12 @@ def update_profile_operation() do
operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation",
security: [%{"oAuth" => ["write:accounts"]}],
parameters: [frontend_name_param(), profile_name_param()],
requestBody: request_body_param,
requestBody: profile_body_param(),
responses: %{
200 =>
Operation.response("Translation", "application/json", %Schema{type: :object})
Operation.response("Settings", "application/json", %Schema{type: :object}),
422 =>
Operation.response("Invalid", "application/json", %Schema{type: :object})
}
}
end
@ -74,6 +77,27 @@ def profile_name_param do
end
def profile_body_param do
request_body("Settings", %Schema{type: :object}, required: true),
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

View file

@ -17,7 +17,8 @@ test "valid" do
user_id: user.id,
frontend_name: frontend_name,
profile_name: profile_name,
settings: settings
settings: settings,
version: 1
}
assert %{valid?: true} = FrontendSettingProfile.changeset(struct, attrs)
@ -35,7 +36,8 @@ test "when settings is too long" do
user_id: user.id,
frontend_name: frontend_name,
profile_name: profile_name,
settings: settings
settings: settings,
version: 1
}
assert %{valid?: false, errors: [settings: {"is too long", _}]} =
@ -53,7 +55,8 @@ test "when frontend name is too short" do
user_id: user.id,
frontend_name: frontend_name,
profile_name: profile_name,
settings: settings
settings: settings,
version: 1
}
assert %{valid?: false, errors: [frontend_name: {"can't be blank", _}]} =
@ -71,12 +74,32 @@ test "when profile name is too short" do
user_id: user.id,
frontend_name: frontend_name,
profile_name: profile_name,
settings: settings
settings: settings,
version: 1
}
assert %{valid?: false, errors: [profile_name: {"can't be blank", _}]} =
FrontendSettingProfile.changeset(struct, attrs)
end
test "when version is negative" do
user = insert(:user)
frontend_name = "test"
profile_name = "test"
settings = %{"test" => "test"}
struct = %FrontendSettingProfile{}
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}", _}]} =
FrontendSettingProfile.changeset(struct, attrs)
end
end
describe "create_or_update/2" do
@ -91,7 +114,8 @@ test "it should create a new record" do
user,
frontend_name,
profile_name,
settings
settings,
1
)
end
@ -104,7 +128,8 @@ test "it should update a record" do
user: user,
frontend_name: frontend_name,
profile_name: profile_name,
settings: %{"test" => "test"}
settings: %{"test" => "test"},
version: 1
)
settings = %{"test" => "test2"}
@ -114,7 +139,8 @@ test "it should update a record" do
user,
frontend_name,
profile_name,
settings
settings,
2
)
end
end
@ -128,14 +154,16 @@ test "it should return all records" do
user: user,
frontend_name: frontend_name,
profile_name: "profileA",
settings: %{"test" => "test"}
settings: %{"test" => "test"},
version: 1
)
insert(:frontend_setting_profile,
user: user,
frontend_name: frontend_name,
profile_name: "profileB",
settings: %{"test" => "test"}
settings: %{"test" => "test"},
version: 1
)
assert [%FrontendSettingProfile{profile_name: "profileA"}, %{profile_name: "profileB"}] =
@ -153,7 +181,8 @@ test "it should return a record" do
user: user,
frontend_name: frontend_name,
profile_name: profile_name,
settings: %{"test" => "test"}
settings: %{"test" => "test"},
version: 1
)
assert %FrontendSettingProfile{profile_name: "profileA"} =

View file

@ -46,25 +46,25 @@ test "it returns 200 if found" do
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("/api/v1/akkoma/frontend_settings/test/test1", %{"settings" => %{"test" => "test2"}, "version" => 1})
|> 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 == %{"test" => "test2"}
%FrontendSettingProfile{settings: settings} = FrontendSettingProfile.get_by_user_and_frontend_name_and_profile_name(user, "test", "test1")
assert response == settings
assert %FrontendSettingProfile{settings: ^settings} = FrontendSettingProfile.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)
response =
conn
|> put("/api/v1/akkoma/frontend_settings/test/test1", %{"settings" => %{"test" => "test2"}, "version" => 1})
|> json_response_and_validate_schema(200)
assert response == %{"test" => "test2"}
%FrontendSettingProfile{settings: settings} = FrontendSettingProfile.get_by_user_and_frontend_name_and_profile_name(user, "test", "test1")
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
end