defmodule PlausibleWeb.Plugins.API.Controllers.CustomPropsTest do use PlausibleWeb.PluginsAPICase, async: true alias PlausibleWeb.Plugins.API.Schemas describe "examples" do test "CustomProp" do assert_schema( Schemas.CustomProp.schema().example, "CustomProp", spec() ) end test "CustomProp.CreateRequest" do assert_schema( Schemas.CustomProp.EnableRequest.schema().example, "CustomProp.EnableRequest", spec() ) end end describe "unauthorized calls" do for {method, url} <- [ {:put, Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable)}, {:delete, Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :disable)} ] do test "unauthorized call: #{method} #{url}", %{conn: conn} do conn |> unquote(method)(unquote(url)) |> json_response(401) |> assert_schema("UnauthorizedError", spec()) end end end describe "business tier" do @describetag :full_build_only test "fails on custom prop enable attempt with insufficient plan", %{ site: site, token: token, conn: conn } do site = Plausible.Repo.preload(site, :owner) insert(:growth_subscription, user: site.owner) url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{ custom_prop: %{key: "author"} } assert_request_schema(payload, "CustomProp.EnableRequest", spec()) conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> put(url, payload) |> json_response(402) |> assert_schema("PaymentRequiredError", spec()) end test "fails on bulk prop enable attempt with insufficient plan", %{ site: site, token: token, conn: conn } do site = Plausible.Repo.preload(site, :owner) insert(:growth_subscription, user: site.owner) url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{ custom_props: [ %{ custom_prop: %{key: "author"} }, %{ custom_prop: %{key: "category"} } ] } conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> put(url, payload) |> json_response(402) |> assert_schema("PaymentRequiredError", spec()) end end describe "put /custom_prop - enable single prop" do test "validates input according to the schema", %{conn: conn, token: token, site: site} do url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> put(url, %{custom_prop: %{typo: "author"}}) |> json_response(422) |> assert_schema("UnprocessableEntityError", spec()) end test "enables single custom prop", %{conn: conn, token: token, site: site} do url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{ custom_prop: %{key: "author"} } assert_request_schema(payload, "CustomProp.EnableRequest", spec()) conn = conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> put(url, payload) resp = conn |> json_response(201) |> assert_schema("CustomProp.ListResponse", spec()) resp.custom_props |> List.first() |> assert_schema("CustomProp", spec()) assert "author" in Plausible.Repo.reload!(site).allowed_event_props end test "is idempotent", %{conn: conn, token: token, site: site} do url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) initial_conn = conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") resp1 = initial_conn |> put( url, %{ custom_prop: %{key: "author"} } ) |> json_response(201) |> assert_schema("CustomProp.ListResponse", spec()) resp1.custom_props |> List.first() |> assert_schema("CustomProp", spec()) assert initial_conn |> put( url, %{ custom_prop: %{key: "author"} } ) |> json_response(201) |> assert_schema("CustomProp.ListResponse", spec()) == resp1 end end describe "put /custom_props - bulk creation" do test "creates many custom props", %{conn: conn, token: token, site: site} do url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{ custom_props: [ %{ custom_prop: %{key: "author"} }, %{ custom_prop: %{key: "rating"} }, %{ custom_prop: %{key: "category"} } ] } assert_request_schema(payload, "CustomProp.EnableRequest.BulkEnable", spec()) conn = conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> put(url, payload) resp = conn |> json_response(201) |> assert_schema("CustomProp.ListResponse", spec()) assert Enum.count(resp.custom_props) == 3 assert [ "author", "rating", "category" ] = Plausible.Repo.reload!(site).allowed_event_props end end describe "delete /custom_props" do test "disable one prop", %{conn: conn, site: site, token: token} do {:ok, ["author"]} = Plausible.Plugins.API.CustomProps.enable(site, "author") url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{custom_prop: %{key: "author"}} conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> delete(url, payload) |> response(204) assert Plausible.Repo.reload!(site).allowed_event_props == [] end test "disable many props", %{conn: conn, site: site, token: token} do {:ok, [_, _, _]} = Plausible.Plugins.API.CustomProps.enable(site, ["author", "category", "third"]) url = Routes.plugins_api_custom_props_url(PlausibleWeb.Endpoint, :enable) payload = %{ custom_props: [ %{custom_prop: %{key: "author"}}, %{custom_prop: %{key: "category"}} ] } conn |> authenticate(site.domain, token) |> put_req_header("content-type", "application/json") |> delete(url, payload) |> response(204) assert Plausible.Repo.reload!(site).allowed_event_props == ["third"] end end end