Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ defmodule PlausibleWeb.Api.ExternalSitesController do
end
end

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

page =
user
|> Teams.Users.teams_query(order_by: :id_desc)
|> paginate(params, @pagination_opts)

json(conn, %{
teams:
Enum.map(page.entries, fn team ->
api_available? =
Plausible.Billing.Feature.StatsAPI.check_availability(team) == :ok

%{
id: team.identifier,
name: Teams.name(team),
api_available: api_available?
}
end),
meta: pagination_meta(page.metadata)
})
end

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

Expand Down
2 changes: 1 addition & 1 deletion lib/plausible/sites.ex
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ defmodule Plausible.Sites do
where(
query,
[team_memberships: tm, guest_memberships: gm, site: s],
(tm.role != :guest and tm.team_id == ^team.id) or gm.site_id == s.id
tm.role != :guest and tm.team_id == ^team.id
)
else
where(
Expand Down
36 changes: 28 additions & 8 deletions lib/plausible/teams/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,38 @@ defmodule Plausible.Teams.Users do
end

def teams(user) do
from(
tm in Teams.Membership,
inner_join: t in assoc(tm, :team),
where: tm.user_id == ^user.id,
where: tm.role != :guest,
select: t,
order_by: [t.name, t.id]
)
user
|> teams_query(order_by: :name)
|> Repo.all()
|> Repo.preload(:owners)
end

def teams_query(user, opts \\ []) do
order_by = Keyword.get(opts, :order_by, :name)

query =
from(
tm in Teams.Membership,
as: :team_membership,
inner_join: t in assoc(tm, :team),
as: :team,
where: tm.user_id == ^user.id,
where: tm.role != :guest,
select: t
)

case order_by do
:name ->
order_by(query, [team: t], [t.name, t.id])

:id_desc ->
order_by(query, [team: t], desc: t.id)

_ ->
query
end
end

def team_member?(user, opts \\ []) do
excluded_team_ids = Keyword.get(opts, :except, [])

Expand Down
1 change: 1 addition & 0 deletions lib/plausible_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ defmodule PlausibleWeb.Router do
pipe_through PlausibleWeb.Plugs.AuthorizePublicAPI

get "/", ExternalSitesController, :index
get "/teams", ExternalSitesController, :teams_index
get "/goals", ExternalSitesController, :goals_index
get "/guests", ExternalSitesController, :guests_index
get "/:site_id", ExternalSitesController, :get_site
Expand Down
8 changes: 0 additions & 8 deletions lib/plausible_web/templates/settings/team_general.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,6 @@
for={@team_name_changeset}
method="post"
>
<div class="w-1/2">
<.input_with_clipboard
name="team-identifier"
id="team-identifier"
label="Team Identifier"
value={@current_team.identifier}
/>
</div>
<.input
readonly={@current_team_role not in [:owner, :admin]}
type="text"
Expand Down
104 changes: 102 additions & 2 deletions test/plausible_web/controllers/api/external_sites_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,68 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
{:ok, api_key: api_key, conn: conn}
end

describe "GET /api/v1/sites/teams" do
test "shows empty list when user is not a member of any team", %{conn: conn} do
conn = get(conn, "/api/v1/sites/teams")

assert json_response(conn, 200) == %{
"teams" => [],
"meta" => %{
"before" => nil,
"after" => nil,
"limit" => 100
}
}
end

test "shows list of teams user is a member of with api availability reflecting team state",
%{conn: conn, user: user} do
user |> subscribe_to_growth_plan()

personal_team = team_of(user)

owner1 =
new_user(
trial_expiry_date: Date.add(Date.utc_today(), -1),
team: [name: "Team Without Stats API"]
)
|> subscribe_to_enterprise_plan(features: [])

team_without_stats = owner1 |> team_of() |> Plausible.Teams.complete_setup()
add_member(team_without_stats, user: user, role: :editor)
owner2 = new_user(team: [name: "Team With Stats API"])
team_with_stats = owner2 |> team_of() |> Plausible.Teams.complete_setup()
add_member(team_with_stats, user: user, role: :owner)

conn = get(conn, "/api/v1/sites/teams")

assert json_response(conn, 200) == %{
"teams" => [
%{
"id" => team_with_stats.identifier,
"name" => "Team With Stats API",
"api_available" => true
},
%{
"id" => team_without_stats.identifier,
"name" => "Team Without Stats API",
"api_available" => false
},
%{
"id" => personal_team.identifier,
"name" => "My Personal Sites",
"api_available" => false
}
],
"meta" => %{
"before" => nil,
"after" => nil,
"limit" => 100
}
}
end
end

describe "POST /api/v1/sites" do
test "can create a site", %{conn: conn} do
conn =
Expand All @@ -28,6 +90,45 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do
}
end

test "can't create site in a team where not permitted to", %{conn: conn, user: user} do
owner = new_user() |> subscribe_to_growth_plan()
team = owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(team, user: user, role: :viewer)

conn =
post(conn, "/api/v1/sites", %{
"team_id" => team.identifier,
"domain" => "some-site.domain",
"timezone" => "Europe/Tallinn"
})

assert json_response(conn, 403) == %{
"error" => "You can't add sites to the selected team."
}
end

test "can create a site under a specific team if permitted", %{conn: conn, user: user} do
_site = new_site(owner: user)

owner = new_user() |> subscribe_to_growth_plan()
team = owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(team, user: user, role: :owner)

conn =
post(conn, "/api/v1/sites", %{
"team_id" => team.identifier,
"domain" => "some-site.domain",
"timezone" => "Europe/Tallinn"
})

assert json_response(conn, 200) == %{
"domain" => "some-site.domain",
"timezone" => "Europe/Tallinn"
}

assert Repo.get_by(Plausible.Site, domain: "some-site.domain").team_id == team.id
end

test "timezone is validated", %{conn: conn} do
conn =
post(conn, "/api/v1/sites", %{
Expand Down Expand Up @@ -580,8 +681,7 @@ defmodule PlausibleWeb.Api.ExternalSitesControllerTest do

assert_matches %{
"sites" => [
%{"domain" => ^other_team_site.domain},
%{"domain" => ^other_site.domain}
%{"domain" => ^other_team_site.domain}
]
} = json_response(conn, 200)
end
Expand Down
10 changes: 6 additions & 4 deletions test/plausible_web/controllers/auth_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,8 @@ defmodule PlausibleWeb.AuthControllerTest do
} do
another_owner = new_user()
another_site = new_site(owner: another_owner)
add_member(another_site.team, user: user, role: :admin)
another_team = another_owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(another_team, user: user, role: :admin)

segment =
insert(:segment,
Expand All @@ -728,7 +729,8 @@ defmodule PlausibleWeb.AuthControllerTest do
} do
another_owner = new_user()
another_site = new_site(owner: another_owner)
add_member(another_site.team, user: user, role: :admin)
another_team = another_owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(another_team, user: user, role: :admin)

segment =
insert(:segment,
Expand All @@ -749,7 +751,7 @@ defmodule PlausibleWeb.AuthControllerTest do
} do
another_owner = new_user()
another_site = new_site(owner: another_owner)
team = team_of(another_owner)
team = another_owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(another_site.team, user: user, role: :owner)

delete(conn, "/me")
Expand All @@ -766,7 +768,7 @@ defmodule PlausibleWeb.AuthControllerTest do
personal_team = team_of(user)
another_owner = new_user()
_another_site = new_site(owner: another_owner)
another_team = team_of(another_owner)
another_team = another_owner |> team_of() |> Plausible.Teams.complete_setup()
add_member(another_team, user: user, role: :owner)

delete(conn, "/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,6 @@ defmodule PlausibleWeb.SettingsControllerTest do
assert html =~ "Team Information"
assert html =~ "Change the name of your team"
assert text_of_attr(html, "input#team_name", "value") == team.name
assert text_of_attr(html, "input#team-identifier", "value") == team.identifier
end

test "POST /settings/team/general/name", %{conn: conn, user: user} do
Expand Down
Loading