mirror of
https://github.com/plausible/analytics.git
synced 2024-11-25 07:06:11 +03:00
Extract schema transitions under delegated namespace (#4788)
* Extract schema transitions under delegated namespace * fixup
This commit is contained in:
parent
799e163eef
commit
78a95eb8fc
@ -13,8 +13,6 @@ defmodule Plausible.Sites do
|
||||
|
||||
require Plausible.Site.UserPreference
|
||||
|
||||
@type list_opt() :: {:filter_by_domain, String.t()}
|
||||
|
||||
def get_by_domain(domain) do
|
||||
Repo.get_by(Site, domain: domain)
|
||||
end
|
||||
@ -71,109 +69,10 @@ defmodule Plausible.Sites do
|
||||
)
|
||||
end
|
||||
|
||||
def list(user, pagination_params, opts \\ []) do
|
||||
if Plausible.Teams.read_team_schemas?(user) do
|
||||
Plausible.Teams.Sites.list(user, pagination_params, opts)
|
||||
else
|
||||
old_list(user, pagination_params, opts)
|
||||
end
|
||||
end
|
||||
defdelegate list(user, pagination_params, opts \\ []), to: Plausible.Teams.Adapter.Read.Sites
|
||||
|
||||
def list_with_invitations(user, pagination_params, opts \\ []) do
|
||||
if Plausible.Teams.read_team_schemas?(user) do
|
||||
Plausible.Teams.Sites.list_with_invitations(user, pagination_params, opts)
|
||||
else
|
||||
old_list_with_invitations(user, pagination_params, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@spec old_list(Auth.User.t(), map(), [list_opt()]) :: Scrivener.Page.t()
|
||||
def old_list(user, pagination_params, opts \\ []) do
|
||||
domain_filter = Keyword.get(opts, :filter_by_domain)
|
||||
|
||||
from(s in Site,
|
||||
left_join: up in Site.UserPreference,
|
||||
on: up.site_id == s.id and up.user_id == ^user.id,
|
||||
inner_join: sm in assoc(s, :memberships),
|
||||
on: sm.user_id == ^user.id,
|
||||
select: %{
|
||||
s
|
||||
| pinned_at: selected_as(up.pinned_at, :pinned_at),
|
||||
entry_type:
|
||||
selected_as(
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? IS NOT NULL THEN 'pinned_site'
|
||||
ELSE 'site'
|
||||
END
|
||||
""",
|
||||
up.pinned_at
|
||||
),
|
||||
:entry_type
|
||||
)
|
||||
},
|
||||
order_by: [asc: selected_as(:entry_type), desc: selected_as(:pinned_at), asc: s.domain],
|
||||
preload: [memberships: sm]
|
||||
)
|
||||
|> maybe_filter_by_domain(domain_filter)
|
||||
|> Repo.paginate(pagination_params)
|
||||
end
|
||||
|
||||
@spec old_list_with_invitations(Auth.User.t(), map(), [list_opt()]) :: Scrivener.Page.t()
|
||||
def old_list_with_invitations(user, pagination_params, opts \\ []) do
|
||||
domain_filter = Keyword.get(opts, :filter_by_domain)
|
||||
|
||||
result =
|
||||
from(s in Site,
|
||||
left_join: up in Site.UserPreference,
|
||||
on: up.site_id == s.id and up.user_id == ^user.id,
|
||||
left_join: i in assoc(s, :invitations),
|
||||
on: i.email == ^user.email,
|
||||
left_join: sm in assoc(s, :memberships),
|
||||
on: sm.user_id == ^user.id,
|
||||
where: not is_nil(sm.id) or not is_nil(i.id),
|
||||
select: %{
|
||||
s
|
||||
| pinned_at: selected_as(up.pinned_at, :pinned_at),
|
||||
entry_type:
|
||||
selected_as(
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? IS NOT NULL THEN 'invitation'
|
||||
WHEN ? IS NOT NULL THEN 'pinned_site'
|
||||
ELSE 'site'
|
||||
END
|
||||
""",
|
||||
i.id,
|
||||
up.pinned_at
|
||||
),
|
||||
:entry_type
|
||||
)
|
||||
},
|
||||
order_by: [asc: selected_as(:entry_type), desc: selected_as(:pinned_at), asc: s.domain],
|
||||
preload: [memberships: sm, invitations: i]
|
||||
)
|
||||
|> maybe_filter_by_domain(domain_filter)
|
||||
|> Repo.paginate(pagination_params)
|
||||
|
||||
# Populating `site` preload on `invitation`
|
||||
# without requesting it from database.
|
||||
# Necessary for invitation modals logic.
|
||||
entries =
|
||||
Enum.map(result.entries, fn
|
||||
%{invitations: [invitation]} = site ->
|
||||
site = %{site | invitations: [], memberships: []}
|
||||
invitation = %{invitation | site: site}
|
||||
%{site | invitations: [invitation]}
|
||||
|
||||
site ->
|
||||
site
|
||||
end)
|
||||
|
||||
%{result | entries: entries}
|
||||
end
|
||||
defdelegate list_with_invitations(user, pagination_params, opts \\ []),
|
||||
to: Plausible.Teams.Adapter.Read.Sites
|
||||
|
||||
@spec for_user_query(Auth.User.t()) :: Ecto.Query.t()
|
||||
def for_user_query(user) do
|
||||
@ -184,13 +83,6 @@ defmodule Plausible.Sites do
|
||||
)
|
||||
end
|
||||
|
||||
defp maybe_filter_by_domain(query, domain)
|
||||
when byte_size(domain) >= 1 and byte_size(domain) <= 64 do
|
||||
where(query, [s], ilike(s.domain, ^"%#{domain}%"))
|
||||
end
|
||||
|
||||
defp maybe_filter_by_domain(query, _), do: query
|
||||
|
||||
def create(user, params) do
|
||||
with :ok <- Quota.ensure_can_add_new_site(user) do
|
||||
Ecto.Multi.new()
|
||||
|
20
lib/plausible/teams/adapter/read/billing.ex
Normal file
20
lib/plausible/teams/adapter/read/billing.ex
Normal file
@ -0,0 +1,20 @@
|
||||
defmodule Plausible.Teams.Adapter.Read.Billing do
|
||||
@moduledoc """
|
||||
Transition adapter for new schema reads
|
||||
"""
|
||||
alias Plausible.Teams
|
||||
|
||||
def check_needs_to_upgrade(user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
team =
|
||||
case Teams.get_by_owner(user) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
|
||||
Teams.Billing.check_needs_to_upgrade(team)
|
||||
else
|
||||
Plausible.Billing.check_needs_to_upgrade(user)
|
||||
end
|
||||
end
|
||||
end
|
71
lib/plausible/teams/adapter/read/ownership.ex
Normal file
71
lib/plausible/teams/adapter/read/ownership.ex
Normal file
@ -0,0 +1,71 @@
|
||||
defmodule Plausible.Teams.Adapter.Read.Ownership do
|
||||
@moduledoc """
|
||||
Transition adapter for new schema reads
|
||||
"""
|
||||
use Plausible
|
||||
alias Plausible.Site
|
||||
alias Plausible.Auth
|
||||
alias Plausible.Teams
|
||||
alias Plausible.Site.Memberships.Invitations
|
||||
|
||||
def ensure_can_take_ownership(site, user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
team =
|
||||
case Teams.get_by_owner(user) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
|
||||
Teams.Invitations.ensure_can_take_ownership(site, team)
|
||||
else
|
||||
Invitations.ensure_can_take_ownership(site, user)
|
||||
end
|
||||
end
|
||||
|
||||
def has_sites?(user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
Teams.Users.has_sites?(user, include_pending?: true)
|
||||
else
|
||||
Site.Memberships.any_or_pending?(user)
|
||||
end
|
||||
end
|
||||
|
||||
def owns_sites?(user, sites) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
Teams.Users.owns_sites?(user, include_pending?: true)
|
||||
else
|
||||
Enum.any?(sites.entries, fn site ->
|
||||
length(site.invitations) > 0 && List.first(site.invitations).role == :owner
|
||||
end) ||
|
||||
Auth.user_owns_sites?(user)
|
||||
end
|
||||
end
|
||||
|
||||
on_ee do
|
||||
def check_feature_access(site, new_owner) do
|
||||
user_or_team =
|
||||
if Teams.read_team_schemas?(new_owner) do
|
||||
case Teams.get_by_owner(new_owner) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
else
|
||||
new_owner
|
||||
end
|
||||
|
||||
missing_features =
|
||||
Plausible.Billing.Quota.Usage.features_usage(nil, [site.id])
|
||||
|> Enum.filter(&(&1.check_availability(user_or_team) != :ok))
|
||||
|
||||
if missing_features == [] do
|
||||
:ok
|
||||
else
|
||||
{:error, {:missing_features, missing_features}}
|
||||
end
|
||||
end
|
||||
else
|
||||
def check_feature_access(_site, _new_owner) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
121
lib/plausible/teams/adapter/read/sites.ex
Normal file
121
lib/plausible/teams/adapter/read/sites.ex
Normal file
@ -0,0 +1,121 @@
|
||||
defmodule Plausible.Teams.Adapter.Read.Sites do
|
||||
@moduledoc """
|
||||
Transition adapter for new schema reads
|
||||
"""
|
||||
import Ecto.Query
|
||||
alias Plausible.Repo
|
||||
alias Plausible.Site
|
||||
alias Plausible.Auth
|
||||
|
||||
def list(user, pagination_params, opts \\ []) do
|
||||
if Plausible.Teams.read_team_schemas?(user) do
|
||||
Plausible.Teams.Sites.list(user, pagination_params, opts)
|
||||
else
|
||||
old_list(user, pagination_params, opts)
|
||||
end
|
||||
end
|
||||
|
||||
def list_with_invitations(user, pagination_params, opts \\ []) do
|
||||
if Plausible.Teams.read_team_schemas?(user) do
|
||||
Plausible.Teams.Sites.list_with_invitations(user, pagination_params, opts)
|
||||
else
|
||||
old_list_with_invitations(user, pagination_params, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@type list_opt() :: {:filter_by_domain, String.t()}
|
||||
@spec old_list(Auth.User.t(), map(), [list_opt()]) :: Scrivener.Page.t()
|
||||
def old_list(user, pagination_params, opts \\ []) do
|
||||
domain_filter = Keyword.get(opts, :filter_by_domain)
|
||||
|
||||
from(s in Site,
|
||||
left_join: up in Site.UserPreference,
|
||||
on: up.site_id == s.id and up.user_id == ^user.id,
|
||||
inner_join: sm in assoc(s, :memberships),
|
||||
on: sm.user_id == ^user.id,
|
||||
select: %{
|
||||
s
|
||||
| pinned_at: selected_as(up.pinned_at, :pinned_at),
|
||||
entry_type:
|
||||
selected_as(
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? IS NOT NULL THEN 'pinned_site'
|
||||
ELSE 'site'
|
||||
END
|
||||
""",
|
||||
up.pinned_at
|
||||
),
|
||||
:entry_type
|
||||
)
|
||||
},
|
||||
order_by: [asc: selected_as(:entry_type), desc: selected_as(:pinned_at), asc: s.domain],
|
||||
preload: [memberships: sm]
|
||||
)
|
||||
|> maybe_filter_by_domain(domain_filter)
|
||||
|> Repo.paginate(pagination_params)
|
||||
end
|
||||
|
||||
@spec old_list_with_invitations(Auth.User.t(), map(), [list_opt()]) :: Scrivener.Page.t()
|
||||
def old_list_with_invitations(user, pagination_params, opts \\ []) do
|
||||
domain_filter = Keyword.get(opts, :filter_by_domain)
|
||||
|
||||
result =
|
||||
from(s in Site,
|
||||
left_join: up in Site.UserPreference,
|
||||
on: up.site_id == s.id and up.user_id == ^user.id,
|
||||
left_join: i in assoc(s, :invitations),
|
||||
on: i.email == ^user.email,
|
||||
left_join: sm in assoc(s, :memberships),
|
||||
on: sm.user_id == ^user.id,
|
||||
where: not is_nil(sm.id) or not is_nil(i.id),
|
||||
select: %{
|
||||
s
|
||||
| pinned_at: selected_as(up.pinned_at, :pinned_at),
|
||||
entry_type:
|
||||
selected_as(
|
||||
fragment(
|
||||
"""
|
||||
CASE
|
||||
WHEN ? IS NOT NULL THEN 'invitation'
|
||||
WHEN ? IS NOT NULL THEN 'pinned_site'
|
||||
ELSE 'site'
|
||||
END
|
||||
""",
|
||||
i.id,
|
||||
up.pinned_at
|
||||
),
|
||||
:entry_type
|
||||
)
|
||||
},
|
||||
order_by: [asc: selected_as(:entry_type), desc: selected_as(:pinned_at), asc: s.domain],
|
||||
preload: [memberships: sm, invitations: i]
|
||||
)
|
||||
|> maybe_filter_by_domain(domain_filter)
|
||||
|> Repo.paginate(pagination_params)
|
||||
|
||||
# Populating `site` preload on `invitation`
|
||||
# without requesting it from database.
|
||||
# Necessary for invitation modals logic.
|
||||
entries =
|
||||
Enum.map(result.entries, fn
|
||||
%{invitations: [invitation]} = site ->
|
||||
site = %{site | invitations: [], memberships: []}
|
||||
invitation = %{invitation | site: site}
|
||||
%{site | invitations: [invitation]}
|
||||
|
||||
site ->
|
||||
site
|
||||
end)
|
||||
|
||||
%{result | entries: entries}
|
||||
end
|
||||
|
||||
defp maybe_filter_by_domain(query, domain)
|
||||
when byte_size(domain) >= 1 and byte_size(domain) <= 64 do
|
||||
where(query, [s], ilike(s.domain, ^"%#{domain}%"))
|
||||
end
|
||||
|
||||
defp maybe_filter_by_domain(query, _), do: query
|
||||
end
|
@ -6,11 +6,7 @@ defmodule PlausibleWeb.Live.Sites do
|
||||
use PlausibleWeb, :live_view
|
||||
import PlausibleWeb.Live.Components.Pagination
|
||||
|
||||
alias Plausible.Auth
|
||||
alias Plausible.Site
|
||||
alias Plausible.Sites
|
||||
alias Plausible.Site.Memberships.Invitations
|
||||
alias Plausible.Teams
|
||||
|
||||
def mount(params, _session, socket) do
|
||||
uri =
|
||||
@ -644,38 +640,11 @@ defmodule PlausibleWeb.Live.Sites do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp has_sites?(user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
Teams.Users.has_sites?(user, include_pending?: true)
|
||||
else
|
||||
Site.Memberships.any_or_pending?(user)
|
||||
end
|
||||
end
|
||||
defdelegate has_sites?(user), to: Plausible.Teams.Adapter.Read.Ownership
|
||||
|
||||
defp owns_sites?(user, sites) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
Teams.Users.owns_sites?(user, include_pending?: true)
|
||||
else
|
||||
Enum.any?(sites.entries, fn site ->
|
||||
length(site.invitations) > 0 && List.first(site.invitations).role == :owner
|
||||
end) ||
|
||||
Auth.user_owns_sites?(user)
|
||||
end
|
||||
end
|
||||
defdelegate owns_sites?(user, sites), to: Plausible.Teams.Adapter.Read.Ownership
|
||||
|
||||
defp check_needs_to_upgrade(user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
team =
|
||||
case Teams.get_by_owner(user) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
|
||||
Teams.Billing.check_needs_to_upgrade(team)
|
||||
else
|
||||
Plausible.Billing.check_needs_to_upgrade(user)
|
||||
end
|
||||
end
|
||||
defdelegate check_needs_to_upgrade(user), to: Plausible.Teams.Adapter.Read.Billing
|
||||
|
||||
defp load_sites(%{assigns: assigns} = socket) do
|
||||
sites =
|
||||
@ -726,32 +695,12 @@ defmodule PlausibleWeb.Live.Sites do
|
||||
|
||||
defp check_limits(invitation, _), do: %{invitation: invitation}
|
||||
|
||||
defp ensure_can_take_ownership(site, user) do
|
||||
if Teams.read_team_schemas?(user) do
|
||||
team =
|
||||
case Teams.get_by_owner(user) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
defdelegate ensure_can_take_ownership(site, user), to: Plausible.Teams.Adapter.Read.Ownership
|
||||
|
||||
Teams.Invitations.ensure_can_take_ownership(site, team)
|
||||
else
|
||||
Invitations.ensure_can_take_ownership(site, user)
|
||||
end
|
||||
end
|
||||
defdelegate check_feature_access(site, user), to: Plausible.Teams.Adapter.Read.Ownership
|
||||
|
||||
defp check_features(%{role: :owner, site: site} = invitation, user) do
|
||||
user_or_team =
|
||||
if Teams.read_team_schemas?(user) do
|
||||
case Teams.get_by_owner(user) do
|
||||
{:ok, team} -> team
|
||||
{:error, _} -> nil
|
||||
end
|
||||
else
|
||||
user
|
||||
end
|
||||
|
||||
case check_feature_access(site, user_or_team) do
|
||||
def check_features(%{role: :owner, site: site} = invitation, user) do
|
||||
case check_feature_access(site, user) do
|
||||
:ok ->
|
||||
%{invitation: invitation}
|
||||
|
||||
@ -765,24 +714,6 @@ defmodule PlausibleWeb.Live.Sites do
|
||||
end
|
||||
end
|
||||
|
||||
if ce?() do
|
||||
defp check_feature_access(_site, _new_owner) do
|
||||
:ok
|
||||
end
|
||||
else
|
||||
defp check_feature_access(site, new_owner) do
|
||||
missing_features =
|
||||
Plausible.Billing.Quota.Usage.features_usage(nil, [site.id])
|
||||
|> Enum.filter(&(&1.check_availability(new_owner) != :ok))
|
||||
|
||||
if missing_features == [] do
|
||||
:ok
|
||||
else
|
||||
{:error, {:missing_features, missing_features}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp set_filter_text(socket, filter_text) do
|
||||
uri = socket.assigns.uri
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user