Skip to content

Commit

Permalink
Add listing sites, goals and custom props to Sites API
Browse files Browse the repository at this point in the history
  • Loading branch information
zoldar committed Jul 2, 2024
1 parent 790984e commit 602fcff
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 49 deletions.
139 changes: 93 additions & 46 deletions extra/lib/plausible_web/controllers/api/external_sites_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,50 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
alias Plausible.Goals
alias PlausibleWeb.Api.Helpers, as: H

def index(conn, params) do
user = conn.assigns.current_user

page = Sites.list(user, params)

json(conn, %{
sites: page.entries,
meta: %{
page_number: page.page_number,
page_size: page.page_size,
total_entries: page.total_entries,
total_pages: page.total_pages
}
})
end

def goals_index(conn, params) do
user = conn.assigns.current_user

with {:ok, site_id} <- expect_param_key(params, "site_id"),
{:ok, site} <- get_site(user, site_id, [:owner, :admin]) do
page =
site
|> Plausible.Goals.for_site_query()
|> Repo.paginate(params)

json(conn, %{
goals: page.entries,
meta: %{
page_number: page.page_number,
page_size: page.page_size,
total_entries: page.total_entries,
total_pages: page.total_pages
}
})
else
{:missing, "site_id"} ->
H.bad_request(conn, "Parameter `site_id` is required to create a shared link")

{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")
end
end

def create_site(conn, params) do
user = conn.assigns[:current_user]

Expand All @@ -29,57 +73,50 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
end

def get_site(conn, %{"site_id" => site_id}) do
site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin])
case get_site(conn.assigns.current_user, site_id, [:owner, :admin]) do
{:ok, site} ->
json(conn, %{
domain: site.domain,
timezone: site.timezone,
allowed_custom_props: site.allowed_event_props || []
})

if site do
json(conn, site)
else
H.not_found(conn, "Site could not be found")
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")
end
end

def delete_site(conn, %{"site_id" => site_id}) do
site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner])
case get_site(conn.assigns.current_user, site_id, [:owner]) do
{:ok, site} ->
{:ok, _} = Plausible.Site.Removal.run(site.domain)
json(conn, %{"deleted" => true})

if site do
{:ok, _} = Plausible.Site.Removal.run(site.domain)
json(conn, %{"deleted" => true})
else
H.not_found(conn, "Site could not be found")
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")
end
end

def update_site(conn, %{"site_id" => site_id} = params) do
# for now this only allows to change the domain
site = Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin])

if site do
case Plausible.Site.Domain.change(site, params["domain"]) do
{:ok, site} ->
json(conn, site)

{:error, changeset} ->
conn
|> put_status(400)
|> json(serialize_errors(changeset))
end
with {:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]),
{:ok, site} <- Plausible.Site.Domain.change(site, params["domain"]) do
json(conn, site)
else
H.not_found(conn, "Site could not be found")
end
end
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")

defp expect_param_key(params, key) do
case Map.fetch(params, key) do
:error -> {:missing, key}
res -> res
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(400)
|> json(serialize_errors(changeset))
end
end

def find_or_create_shared_link(conn, params) do
with {:ok, site_id} <- expect_param_key(params, "site_id"),
{:ok, link_name} <- expect_param_key(params, "name"),
site when not is_nil(site) <-
Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) do
{:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]) do
shared_link = Repo.get_by(Plausible.Site.SharedLink, site_id: site.id, name: link_name)

shared_link =
Expand All @@ -96,7 +133,7 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
})
end
else
nil ->
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")

{:missing, "site_id"} ->
Expand All @@ -113,12 +150,11 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
def find_or_create_goal(conn, params) do
with {:ok, site_id} <- expect_param_key(params, "site_id"),
{:ok, _} <- expect_param_key(params, "goal_type"),
site when not is_nil(site) <-
Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]),
{:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]),
{:ok, goal} <- Goals.find_or_create(site, params) do
json(conn, goal)
else
nil ->
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")

{:missing, param} ->
Expand All @@ -132,19 +168,16 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
def delete_goal(conn, params) do
with {:ok, site_id} <- expect_param_key(params, "site_id"),
{:ok, goal_id} <- expect_param_key(params, "goal_id"),
site when not is_nil(site) <-
Sites.get_for_user(conn.assigns[:current_user].id, site_id, [:owner, :admin]) do
case Goals.delete(goal_id, site) do
:ok ->
json(conn, %{"deleted" => true})

{:error, :not_found} ->
H.not_found(conn, "Goal could not be found")
end
{:ok, site} <- get_site(conn.assigns.current_user, site_id, [:owner, :admin]),
:ok <- Goals.delete(goal_id, site) do
json(conn, %{"deleted" => true})
else
nil ->
{:error, :site_not_found} ->
H.not_found(conn, "Site could not be found")

{:error, :not_found} ->
H.not_found(conn, "Goal could not be found")

{:missing, "site_id"} ->
H.bad_request(conn, "Parameter `site_id` is required to delete a goal")

Expand All @@ -156,9 +189,23 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
end
end

defp get_site(user, site_id, roles) do
case Sites.get_for_user(user.id, site_id, roles) do
nil -> {:error, :site_not_found}
site -> {:ok, site}
end
end

defp serialize_errors(changeset) do
{field, {msg, _opts}} = List.first(changeset.errors)
error_msg = Atom.to_string(field) <> ": " <> msg
%{"error" => error_msg}
end

defp expect_param_key(params, key) do
case Map.fetch(params, key) do
:error -> {:missing, key}
res -> res
end
end
end
2 changes: 2 additions & 0 deletions lib/plausible_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ defmodule PlausibleWeb.Router do
scope assigns: %{api_scope: "sites:read:*"} do
pipe_through PlausibleWeb.Plugs.AuthorizePublicAPI

get "/", ExternalSitesController, :index
get "/:site_id", ExternalSitesController, :get_site
get "/goals", ExternalSitesController, :goals_index
end

scope assigns: %{api_scope: "sites:provision:*"} do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
test "get a site by its domain", %{conn: conn, site: site} do
conn = get(conn, "/api/v1/sites/" <> site.domain)

assert json_response(conn, 200) == %{"domain" => site.domain, "timezone" => site.timezone}
assert json_response(conn, 200) == %{
"domain" => site.domain,
"timezone" => site.timezone,
"allowed_custom_props" => []
}
end

test "get a site by old site_id after domain change", %{conn: conn, site: site} do
Expand All @@ -506,7 +510,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do

conn = get(conn, "/api/v1/sites/" <> old_domain)

assert json_response(conn, 200) == %{"domain" => new_domain, "timezone" => site.timezone}
assert json_response(conn, 200) == %{
"domain" => new_domain,
"timezone" => site.timezone,
"allowed_custom_props" => []
}
end

test "get a site with basic scope config", %{conn: conn, user: user, site: site} do
Expand All @@ -517,7 +525,11 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
|> Plug.Conn.put_req_header("authorization", "Bearer #{api_key.key}")
|> get("/api/v1/sites/" <> site.domain)

assert json_response(conn, 200) == %{"domain" => site.domain, "timezone" => site.timezone}
assert json_response(conn, 200) == %{
"domain" => site.domain,
"timezone" => site.timezone,
"allowed_custom_props" => []
}
end

test "is 404 when site cannot be found", %{conn: conn} do
Expand Down

0 comments on commit 602fcff

Please sign in to comment.