From 11a1996bf5f283099fd9ecbb5c64e051ec46a5df Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 8 Mar 2022 20:55:41 -0500 Subject: [PATCH] Implement update announcement admin api --- lib/pleroma/announcement.ex | 22 +++-- .../controllers/announcement_controller.ex | 37 ++++++--- .../admin/announcement_operation.ex | 49 ++++++++++-- lib/pleroma/web/router.ex | 1 + .../announcement_controller_test.exs | 80 ++++++++++++++++++- test/support/factory.ex | 6 +- 6 files changed, 169 insertions(+), 26 deletions(-) diff --git a/lib/pleroma/announcement.ex b/lib/pleroma/announcement.ex index 85500751e..8c15d5bdf 100644 --- a/lib/pleroma/announcement.ex +++ b/lib/pleroma/announcement.ex @@ -24,18 +24,20 @@ defmodule Pleroma.Announcement do def change(struct, params \\ %{}) do struct - |> cast(validate_params(params), [:data, :starts_at, :ends_at]) + |> cast(validate_params(struct, params), [:data, :starts_at, :ends_at]) |> validate_required([:data]) end - defp validate_params(params) do - base_struct = %{ - "content" => "", - "all_day" => false - } + defp validate_params(struct, params) do + base_data = + %{ + "content" => "", + "all_day" => false + } + |> Map.merge((struct && struct.data) || %{}) merged_data = - Map.merge(base_struct, params.data) + Map.merge(base_data, params.data) |> Map.take(["content", "all_day"]) params @@ -48,6 +50,12 @@ defmodule Pleroma.Announcement do Repo.insert(changeset) end + def update(announcement, params) do + changeset = change(announcement, params) + + Repo.update(changeset) + end + def list_all do __MODULE__ |> Repo.all() diff --git a/lib/pleroma/web/admin_api/controllers/announcement_controller.ex b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex index ad94e2642..6195e582a 100644 --- a/lib/pleroma/web/admin_api/controllers/announcement_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementController do alias Pleroma.Web.Plugs.OAuthScopesPlug plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:create, :delete]) + plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:create, :delete, :change]) plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action in [:index, :show]) action_fallback(Pleroma.Web.AdminAPI.FallbackController) @@ -33,16 +33,7 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementController do end def create(%{body_params: params} = conn, _params) do - data = - %{} - |> Pleroma.Maps.put_if_present("content", params, &Map.fetch(&1, :content)) - |> Pleroma.Maps.put_if_present("all_day", params, &Map.fetch(&1, :all_day)) - - add_params = - params - |> Map.merge(%{data: data}) - - with {:ok, announcement} <- Announcement.add(add_params) do + with {:ok, announcement} <- Announcement.add(change_params(params)) do render(conn, "show.json", announcement: announcement) else _ -> @@ -50,6 +41,30 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementController do end end + def change_params(orig_params) do + data = + %{} + |> Pleroma.Maps.put_if_present("content", orig_params, &Map.fetch(&1, :content)) + |> Pleroma.Maps.put_if_present("all_day", orig_params, &Map.fetch(&1, :all_day)) + + orig_params + |> Map.merge(%{data: data}) + end + + def change(%{body_params: params} = conn, %{id: id} = _params) do + with announcement <- Announcement.get_by_id(id), + {:exists, true} <- {:exists, not is_nil(announcement)}, + {:ok, announcement} <- Announcement.update(announcement, change_params(params)) do + render(conn, "show.json", announcement: announcement) + else + {:exists, false} -> + {:error, :not_found} + + _ -> + {:error, 400} + end + end + def delete(conn, %{id: id} = _params) do case Announcement.delete_by_id(id) do :ok -> diff --git a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex index 8179a0e7b..cdf04d357 100644 --- a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex @@ -89,17 +89,54 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do } end + def change_operation do + %Operation{ + tags: ["Announcement managment"], + summary: "Change one announcement", + operationId: "AdminAPI.AnnouncementController.change", + security: [%{"oAuth" => ["admin:write"]}], + parameters: [ + Operation.parameter( + :id, + :path, + :string, + "announcement id" + ) + | admin_api_params() + ], + requestBody: request_body("Parameters", change_request(), required: true), + responses: %{ + 200 => Operation.response("Response", "application/json", Announcement), + 400 => Operation.response("Bad Request", "application/json", ApiError), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + defp create_or_change_props do + %{ + content: %Schema{type: :string}, + starts_at: %Schema{type: :string, format: "date-time", nullable: true}, + ends_at: %Schema{type: :string, format: "date-time", nullable: true}, + all_day: %Schema{type: :boolean} + } + end + def create_request do %Schema{ title: "AnnouncementCreateRequest", type: :object, required: [:content], - properties: %{ - content: %Schema{type: :string}, - starts_at: %Schema{type: :string, format: "date-time"}, - ends_at: %Schema{type: :string, format: "date-time"}, - all_day: %Schema{type: :boolean} - } + properties: create_or_change_props() + } + end + + def change_request do + %Schema{ + title: "AnnouncementChangeRequest", + type: :object, + properties: create_or_change_props() } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 51a9dec6b..af56494a2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -233,6 +233,7 @@ defmodule Pleroma.Web.Router do get("/announcements", AnnouncementController, :index) post("/announcements", AnnouncementController, :create) get("/announcements/:id", AnnouncementController, :show) + patch("/announcements/:id", AnnouncementController, :change) delete("/announcements/:id", AnnouncementController, :delete) end diff --git a/test/pleroma/web/admin_api/controllers/announcement_controller_test.exs b/test/pleroma/web/admin_api/controllers/announcement_controller_test.exs index 5c9d50120..c6b2163d0 100644 --- a/test/pleroma/web/admin_api/controllers/announcement_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/announcement_controller_test.exs @@ -69,13 +69,91 @@ defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do _response = conn - |> get("/api/v1/pleroma/admin/announcements/#{id}xxx") + |> delete("/api/v1/pleroma/admin/announcements/#{id}xxx") |> json_response_and_validate_schema(:not_found) assert %{id: ^id} = Pleroma.Announcement.get_by_id(id) end end + describe "PATCH /api/v1/pleroma/admin/announcements/:id" do + test "it returns not found for non-existent id", %{conn: conn} do + %{id: id} = insert(:announcement) + + _response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/v1/pleroma/admin/announcements/#{id}xxx", %{}) + |> json_response_and_validate_schema(:not_found) + + assert %{id: ^id} = Pleroma.Announcement.get_by_id(id) + end + + test "it updates a field", %{conn: conn} do + %{id: id} = insert(:announcement) + + now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + starts_at = NaiveDateTime.add(now, -10, :second) + + _response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/v1/pleroma/admin/announcements/#{id}", %{ + starts_at: NaiveDateTime.to_iso8601(starts_at) + }) + |> json_response_and_validate_schema(:ok) + + new = Pleroma.Announcement.get_by_id(id) + + assert NaiveDateTime.compare(new.starts_at, starts_at) == :eq + end + + test "it updates a data field", %{conn: conn} do + %{id: id} = announcement = insert(:announcement, data: %{"all_day" => true}) + + assert announcement.data["all_day"] == true + + new_content = "new content" + + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/v1/pleroma/admin/announcements/#{id}", %{ + content: new_content + }) + |> json_response_and_validate_schema(:ok) + + assert response["content"] == new_content + assert response["all_day"] == true + + new = Pleroma.Announcement.get_by_id(id) + + assert new.data["content"] == new_content + assert new.data["all_day"] == true + end + + test "it nullifies a nullable field", %{conn: conn} do + now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) + starts_at = NaiveDateTime.add(now, -10, :second) + + %{id: id} = insert(:announcement, starts_at: starts_at) + + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/v1/pleroma/admin/announcements/#{id}", %{ + starts_at: nil + }) + |> json_response_and_validate_schema(:ok) + + assert response["starts_at"] == nil + + new = Pleroma.Announcement.get_by_id(id) + + assert new.starts_at == nil + end + end + describe "POST /api/v1/pleroma/admin/announcements" do test "it creates an announcement", %{conn: conn} do content = "test post announcement api" diff --git a/test/support/factory.ex b/test/support/factory.ex index 64b0049ac..d28a56345 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -628,7 +628,11 @@ defmodule Pleroma.Factory do } end - def announcement_factory(params \\ %{}, data \\ %{}) do + def announcement_factory(params \\ %{}) do + data = Map.get(params, :data, %{}) + + {_, params} = Map.pop(params, :data) + %Pleroma.Announcement{ data: Map.merge(%{"content" => "test announcement", "all_day" => false}, data) }