mirror of
https://github.com/plausible/analytics.git
synced 2024-11-22 02:27:57 +03:00
Switch reads in invitation creation logic to teams behind FF + loosely related fixes 🍌 (#4845)
* Reduce "choose plan" reliance on the old schema * Fix team members usage computation on teams schema * Switch CreateInvitation to reading from team schemas behind FF (WIP) * Allow test-inviting one guest into multiple sites * Convert another test case where team members count is wrong cc @zoldar * WIP: support site transfer notification e-mails * Even more strict SiteTransfer fetching * Make skipping permissions work * Make CreateInvitation read from team schemas behind FF fully * Fix passing options to `check_invitation_permissions` * Fix allowance check for pageview usage for active or recently ended trial case * Fix `check_invitation_permissions` * Remove no longer relevant invite implementations for Teams --------- Co-authored-by: Adrian Gruntkowski <adrian.gruntkowski@gmail.com>
This commit is contained in:
parent
5072613c4f
commit
4d9ea15e9e
@ -6,7 +6,6 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
|||||||
|
|
||||||
alias Plausible.Auth.{User, Invitation}
|
alias Plausible.Auth.{User, Invitation}
|
||||||
alias Plausible.{Site, Sites, Site.Membership}
|
alias Plausible.{Site, Sites, Site.Membership}
|
||||||
alias Plausible.Site.Memberships.Invitations
|
|
||||||
alias Plausible.Billing.Quota
|
alias Plausible.Billing.Quota
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
use Plausible
|
use Plausible
|
||||||
@ -73,24 +72,49 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
|||||||
attrs = %{email: invitee_email, role: role, site_id: site.id, inviter_id: inviter.id}
|
attrs = %{email: invitee_email, role: role, site_id: site.id, inviter_id: inviter.id}
|
||||||
|
|
||||||
with site <- Plausible.Repo.preload(site, :owner),
|
with site <- Plausible.Repo.preload(site, :owner),
|
||||||
:ok <- check_invitation_permissions(site, inviter, role, opts),
|
:ok <-
|
||||||
:ok <- check_team_member_limit(site, role, invitee_email),
|
Plausible.Teams.Adapter.Read.Invitations.check_invitation_permissions(
|
||||||
|
site,
|
||||||
|
inviter,
|
||||||
|
role,
|
||||||
|
opts
|
||||||
|
),
|
||||||
|
:ok <-
|
||||||
|
Plausible.Teams.Adapter.Read.Invitations.check_team_member_limit(
|
||||||
|
inviter,
|
||||||
|
site,
|
||||||
|
role,
|
||||||
|
invitee_email
|
||||||
|
),
|
||||||
invitee = Plausible.Auth.find_user_by(email: invitee_email),
|
invitee = Plausible.Auth.find_user_by(email: invitee_email),
|
||||||
:ok <- Invitations.ensure_transfer_valid(site, invitee, role),
|
:ok <-
|
||||||
:ok <- ensure_new_membership(site, invitee, role),
|
Plausible.Teams.Adapter.Read.Invitations.ensure_transfer_valid(
|
||||||
|
inviter,
|
||||||
|
site,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
),
|
||||||
|
:ok <-
|
||||||
|
Plausible.Teams.Adapter.Read.Invitations.ensure_new_membership(
|
||||||
|
inviter,
|
||||||
|
site,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
),
|
||||||
%Ecto.Changeset{} = changeset <- Invitation.new(attrs),
|
%Ecto.Changeset{} = changeset <- Invitation.new(attrs),
|
||||||
{:ok, invitation} <- Plausible.Repo.insert(changeset) do
|
{:ok, invitation} <- Plausible.Repo.insert(changeset) do
|
||||||
send_invitation_email(invitation, invitee)
|
|
||||||
|
|
||||||
Plausible.Teams.Invitations.invite_sync(site, invitation)
|
Plausible.Teams.Invitations.invite_sync(site, invitation)
|
||||||
|
|
||||||
|
Plausible.Teams.Adapter.Read.Invitations.send_invitation_email(inviter, invitation, invitee)
|
||||||
|
|
||||||
invitation
|
invitation
|
||||||
else
|
else
|
||||||
{:error, cause} -> Plausible.Repo.rollback(cause)
|
{:error, cause} -> Plausible.Repo.rollback(cause)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_invitation_permissions(site, inviter, requested_role, opts) do
|
@doc false
|
||||||
|
def check_invitation_permissions(site, inviter, requested_role, opts) do
|
||||||
check_permissions? = Keyword.get(opts, :check_permissions, true)
|
check_permissions? = Keyword.get(opts, :check_permissions, true)
|
||||||
|
|
||||||
if check_permissions? do
|
if check_permissions? do
|
||||||
@ -107,7 +131,8 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_invitation_email(invitation, invitee) do
|
@doc false
|
||||||
|
def send_invitation_email(invitation, invitee) do
|
||||||
invitation = Plausible.Repo.preload(invitation, [:site, :inviter])
|
invitation = Plausible.Repo.preload(invitation, [:site, :inviter])
|
||||||
|
|
||||||
email =
|
email =
|
||||||
@ -140,11 +165,12 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
|||||||
Plausible.Mailer.send(email)
|
Plausible.Mailer.send(email)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_new_membership(_site, _invitee, :owner) do
|
@doc false
|
||||||
|
def ensure_new_membership(_site, _invitee, :owner) do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_new_membership(site, invitee, _role) do
|
def ensure_new_membership(site, invitee, _role) do
|
||||||
if invitee && Sites.is_member?(invitee.id, site) do
|
if invitee && Sites.is_member?(invitee.id, site) do
|
||||||
{:error, :already_a_member}
|
{:error, :already_a_member}
|
||||||
else
|
else
|
||||||
@ -152,11 +178,12 @@ defmodule Plausible.Site.Memberships.CreateInvitation do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_team_member_limit(_site, :owner, _invitee_email) do
|
@doc false
|
||||||
|
def check_team_member_limit(_site, :owner, _invitee_email) do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_team_member_limit(site, _role, invitee_email) do
|
def check_team_member_limit(site, _role, invitee_email) do
|
||||||
site = Plausible.Repo.preload(site, :owner)
|
site = Plausible.Repo.preload(site, :owner)
|
||||||
limit = Quota.Limits.team_member_limit(site.owner)
|
limit = Quota.Limits.team_member_limit(site.owner)
|
||||||
usage = Quota.Usage.team_member_usage(site.owner, exclude_emails: [invitee_email])
|
usage = Quota.Usage.team_member_usage(site.owner, exclude_emails: [invitee_email])
|
||||||
|
@ -16,7 +16,9 @@ defmodule Plausible.Teams do
|
|||||||
|
|
||||||
def on_trial?(team) do
|
def on_trial?(team) do
|
||||||
team = with_subscription(team)
|
team = with_subscription(team)
|
||||||
not Plausible.Billing.Subscriptions.active?(team.subscription) && trial_days_left(team) >= 0
|
|
||||||
|
not Plausible.Billing.Subscriptions.active?(team.subscription) &&
|
||||||
|
trial_days_left(team) >= 0
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
def on_trial?(_), do: true
|
def on_trial?(_), do: true
|
||||||
|
@ -4,6 +4,20 @@ defmodule Plausible.Teams.Adapter.Read.Billing do
|
|||||||
"""
|
"""
|
||||||
use Plausible.Teams.Adapter
|
use Plausible.Teams.Adapter
|
||||||
|
|
||||||
|
def quota_usage(user, opts \\ []) do
|
||||||
|
switch(user,
|
||||||
|
team_fn: &Plausible.Teams.Billing.quota_usage(&1, opts),
|
||||||
|
user_fn: &Plausible.Billing.Quota.Usage.usage(&1, opts)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def allow_next_upgrade_override?(user) do
|
||||||
|
switch(user,
|
||||||
|
team_fn: &(&1 && &1.allow_next_upgrade_override),
|
||||||
|
user_fn: & &1.allow_next_upgrade_override
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def change_plan(user, new_plan_id) do
|
def change_plan(user, new_plan_id) do
|
||||||
switch(user,
|
switch(user,
|
||||||
team_fn: &Plausible.Teams.Billing.change_plan(&1, new_plan_id),
|
team_fn: &Plausible.Teams.Billing.change_plan(&1, new_plan_id),
|
||||||
|
120
lib/plausible/teams/adapter/read/invitations.ex
Normal file
120
lib/plausible/teams/adapter/read/invitations.ex
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
defmodule Plausible.Teams.Adapter.Read.Invitations do
|
||||||
|
@moduledoc """
|
||||||
|
Transition adapter for new schema reads
|
||||||
|
"""
|
||||||
|
use Plausible
|
||||||
|
use Plausible.Teams.Adapter
|
||||||
|
|
||||||
|
alias Plausible.Repo
|
||||||
|
|
||||||
|
def check_invitation_permissions(site, inviter, role, opts) do
|
||||||
|
switch(
|
||||||
|
inviter,
|
||||||
|
team_fn: fn _ ->
|
||||||
|
Plausible.Teams.Invitations.check_invitation_permissions(
|
||||||
|
site,
|
||||||
|
inviter,
|
||||||
|
role,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
user_fn: fn _ ->
|
||||||
|
Plausible.Site.Memberships.CreateInvitation.check_invitation_permissions(
|
||||||
|
site,
|
||||||
|
inviter,
|
||||||
|
role,
|
||||||
|
opts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_team_member_limit(inviter, site, role, invitee_email) do
|
||||||
|
switch(
|
||||||
|
inviter,
|
||||||
|
team_fn: fn _ ->
|
||||||
|
site_team = Repo.preload(site, :team).team
|
||||||
|
|
||||||
|
Plausible.Teams.Invitations.check_team_member_limit(
|
||||||
|
site_team,
|
||||||
|
role,
|
||||||
|
invitee_email
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
user_fn: fn _ ->
|
||||||
|
Plausible.Site.Memberships.CreateInvitation.check_team_member_limit(
|
||||||
|
site,
|
||||||
|
role,
|
||||||
|
invitee_email
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_transfer_valid(inviter, site, invitee, role) do
|
||||||
|
switch(
|
||||||
|
inviter,
|
||||||
|
team_fn: fn _ ->
|
||||||
|
site_team = Repo.preload(site, :team).team
|
||||||
|
|
||||||
|
Plausible.Teams.Invitations.ensure_transfer_valid(
|
||||||
|
site_team,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
user_fn: fn _ ->
|
||||||
|
Plausible.Site.Memberships.Invitations.ensure_transfer_valid(
|
||||||
|
site,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_new_membership(inviter, site, invitee, role) do
|
||||||
|
switch(
|
||||||
|
inviter,
|
||||||
|
team_fn: fn _ ->
|
||||||
|
Plausible.Teams.Invitations.ensure_new_membership(
|
||||||
|
site,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
user_fn: fn _ ->
|
||||||
|
Plausible.Site.Memberships.CreateInvitation.ensure_new_membership(
|
||||||
|
site,
|
||||||
|
invitee,
|
||||||
|
role
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_invitation_email(inviter, invitation, invitee) do
|
||||||
|
switch(
|
||||||
|
inviter,
|
||||||
|
team_fn: fn _ ->
|
||||||
|
if invitation.role == :owner do
|
||||||
|
Teams.SiteTransfer
|
||||||
|
|> Repo.get_by!(transfer_id: invitation.invitation_id, initiator_id: inviter.id)
|
||||||
|
|> Repo.preload([:site, :initiator])
|
||||||
|
|> Plausible.Teams.Invitations.send_invitation_email(invitee)
|
||||||
|
else
|
||||||
|
Teams.GuestInvitation
|
||||||
|
|> Repo.get_by!(invitation_id: invitation.invitation_id)
|
||||||
|
|> Repo.preload([:site, team_invitation: :inviter])
|
||||||
|
|> Plausible.Teams.Invitations.send_invitation_email(invitee)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
user_fn: fn _ ->
|
||||||
|
Plausible.Site.Memberships.CreateInvitation.send_invitation_email(
|
||||||
|
invitation,
|
||||||
|
invitee
|
||||||
|
)
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
27
lib/plausible/teams/adapter/read/teams.ex
Normal file
27
lib/plausible/teams/adapter/read/teams.ex
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
defmodule Plausible.Teams.Adapter.Read.Teams do
|
||||||
|
@moduledoc """
|
||||||
|
Transition adapter for new schema reads
|
||||||
|
"""
|
||||||
|
use Plausible.Teams.Adapter
|
||||||
|
|
||||||
|
def trial_expiry_date(user) do
|
||||||
|
switch(user,
|
||||||
|
team_fn: &(&1 && &1.trial_expiry_date),
|
||||||
|
user_fn: & &1.trial_expiry_date
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_trial?(user) do
|
||||||
|
switch(user,
|
||||||
|
team_fn: &Plausible.Teams.on_trial?/1,
|
||||||
|
user_fn: &Plausible.Users.on_trial?/1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def trial_days_left(user) do
|
||||||
|
switch(user,
|
||||||
|
team_fn: &Plausible.Teams.trial_days_left/1,
|
||||||
|
user_fn: &Plausible.Users.trial_days_left/1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -252,7 +252,9 @@ defmodule Plausible.Teams.Billing do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def team_member_usage(team, opts) do
|
def team_member_usage(team, opts) do
|
||||||
exclude_emails = Keyword.get(opts, :exclude_emails, [])
|
{:ok, owner} = Teams.Sites.get_owner(team)
|
||||||
|
exclude_emails = Keyword.get(opts, :exclude_emails, []) ++ [owner.email]
|
||||||
|
|
||||||
pending_site_ids = Keyword.get(opts, :pending_ownership_site_ids, [])
|
pending_site_ids = Keyword.get(opts, :pending_ownership_site_ids, [])
|
||||||
|
|
||||||
team
|
team
|
||||||
@ -320,7 +322,7 @@ defmodule Plausible.Teams.Billing do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def features_usage(user, site_ids \\ nil)
|
def features_usage(team, site_ids \\ nil)
|
||||||
|
|
||||||
def features_usage(%Teams.Team{} = team, nil) do
|
def features_usage(%Teams.Team{} = team, nil) do
|
||||||
owned_site_ids = team |> Teams.owned_sites() |> Enum.map(& &1.id)
|
owned_site_ids = team |> Teams.owned_sites() |> Enum.map(& &1.id)
|
||||||
@ -331,8 +333,16 @@ defmodule Plausible.Teams.Billing do
|
|||||||
site_scoped_feature_usage = features_usage(nil, owned_site_ids)
|
site_scoped_feature_usage = features_usage(nil, owned_site_ids)
|
||||||
|
|
||||||
stats_api_used? =
|
stats_api_used? =
|
||||||
from(a in Plausible.Auth.ApiKey, where: a.team_id == ^team.id)
|
Plausible.Repo.exists?(
|
||||||
|> Plausible.Repo.exists?()
|
from tm in Plausible.Teams.Membership,
|
||||||
|
as: :team_membership,
|
||||||
|
where: tm.team_id == ^team.id,
|
||||||
|
where:
|
||||||
|
exists(
|
||||||
|
from ak in Plausible.Auth.ApiKey,
|
||||||
|
where: ak.user_id == parent_as(:team_membership).user_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if stats_api_used? do
|
if stats_api_used? do
|
||||||
site_scoped_feature_usage ++ [Feature.StatsAPI]
|
site_scoped_feature_usage ++ [Feature.StatsAPI]
|
||||||
@ -345,36 +355,47 @@ defmodule Plausible.Teams.Billing do
|
|||||||
Plausible.Billing.Quota.Usage.features_usage(nil, owned_site_ids)
|
Plausible.Billing.Quota.Usage.features_usage(nil, owned_site_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp query_team_member_emails(team, site_ids, exclude_emails) do
|
defp query_team_member_emails(team, pending_ownership_site_ids, exclude_emails) do
|
||||||
|
pending_owner_memberships_q =
|
||||||
|
from s in Plausible.Site,
|
||||||
|
inner_join: t in assoc(s, :team),
|
||||||
|
inner_join: tm in assoc(t, :team_memberships),
|
||||||
|
inner_join: u in assoc(tm, :user),
|
||||||
|
where: s.id in ^pending_ownership_site_ids,
|
||||||
|
where: tm.role == :owner,
|
||||||
|
where: u.email not in ^exclude_emails,
|
||||||
|
select: %{email: u.email}
|
||||||
|
|
||||||
pending_memberships_q =
|
pending_memberships_q =
|
||||||
from tm in Teams.Membership,
|
from tm in Teams.Membership,
|
||||||
inner_join: u in assoc(tm, :user),
|
inner_join: u in assoc(tm, :user),
|
||||||
inner_join: gm in assoc(tm, :guest_memberships),
|
left_join: gm in assoc(tm, :guest_memberships),
|
||||||
where: gm.site_id in ^site_ids and tm.role != :owner,
|
where: gm.site_id in ^pending_ownership_site_ids,
|
||||||
where: u.email not in ^exclude_emails,
|
where: u.email not in ^exclude_emails,
|
||||||
select: %{email: u.email}
|
select: %{email: u.email}
|
||||||
|
|
||||||
pending_invitations_q =
|
pending_invitations_q =
|
||||||
from ti in Teams.Invitation,
|
from ti in Teams.Invitation,
|
||||||
inner_join: gi in assoc(ti, :guest_invitations),
|
inner_join: gi in assoc(ti, :guest_invitations),
|
||||||
where: gi.site_id in ^site_ids and ti.role != :owner,
|
where: gi.site_id in ^pending_ownership_site_ids,
|
||||||
where: ti.email not in ^exclude_emails,
|
where: ti.email not in ^exclude_emails,
|
||||||
select: %{email: ti.email}
|
select: %{email: ti.email}
|
||||||
|
|
||||||
team_memberships_q =
|
team_memberships_q =
|
||||||
from tm in Teams.Membership,
|
from tm in Teams.Membership,
|
||||||
inner_join: u in assoc(tm, :user),
|
inner_join: u in assoc(tm, :user),
|
||||||
where: tm.team_id == ^team.id and tm.role != :owner,
|
where: tm.team_id == ^team.id,
|
||||||
where: u.email not in ^exclude_emails,
|
where: u.email not in ^exclude_emails,
|
||||||
select: %{email: u.email}
|
select: %{email: u.email}
|
||||||
|
|
||||||
team_invitations_q =
|
team_invitations_q =
|
||||||
from ti in Teams.Invitation,
|
from ti in Teams.Invitation,
|
||||||
where: ti.team_id == ^team.id and ti.role != :owner,
|
where: ti.team_id == ^team.id,
|
||||||
where: ti.email not in ^exclude_emails,
|
where: ti.email not in ^exclude_emails,
|
||||||
select: %{email: ti.email}
|
select: %{email: ti.email}
|
||||||
|
|
||||||
pending_memberships_q
|
pending_memberships_q
|
||||||
|
|> union(^pending_owner_memberships_q)
|
||||||
|> union(^pending_invitations_q)
|
|> union(^pending_invitations_q)
|
||||||
|> union(^team_memberships_q)
|
|> union(^team_memberships_q)
|
||||||
|> union(^team_invitations_q)
|
|> union(^team_invitations_q)
|
||||||
|
@ -3,41 +3,10 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Plausible.Auth
|
|
||||||
alias Plausible.Billing
|
alias Plausible.Billing
|
||||||
alias Plausible.Repo
|
alias Plausible.Repo
|
||||||
alias Plausible.Teams
|
alias Plausible.Teams
|
||||||
|
|
||||||
def invite(site, inviter, invitee_email, role, opts \\ [])
|
|
||||||
|
|
||||||
def invite(site, initiator, invitee_email, :owner, opts) do
|
|
||||||
check_permissions? = opts[:check_permissions]
|
|
||||||
site = Repo.preload(site, :team)
|
|
||||||
|
|
||||||
with :ok <- check_transfer_permissions(site.team, initiator, check_permissions?),
|
|
||||||
new_owner = Plausible.Auth.find_user_by(email: invitee_email),
|
|
||||||
:ok <- ensure_transfer_valid(site.team, new_owner),
|
|
||||||
{:ok, site_transfer} <- create_site_transfer(site, initiator, invitee_email) do
|
|
||||||
send_transfer_init_email(site_transfer, new_owner)
|
|
||||||
{:ok, site_transfer}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def invite(site, inviter, invitee_email, role, opts) do
|
|
||||||
check_permissions? = opts[:check_permissions]
|
|
||||||
site = Repo.preload(site, :team)
|
|
||||||
role = translate_role(role)
|
|
||||||
|
|
||||||
with :ok <- check_invitation_permissions(site.team, inviter, check_permissions?),
|
|
||||||
:ok <- check_team_member_limit(site.team, role, invitee_email),
|
|
||||||
invitee = Auth.find_user_by(email: invitee_email),
|
|
||||||
:ok <- ensure_new_membership(site, invitee, role),
|
|
||||||
{:ok, guest_invitation} <- create_invitation(site, invitee_email, role, inviter) do
|
|
||||||
send_invitation_email(guest_invitation, invitee)
|
|
||||||
{:ok, guest_invitation}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def invite_sync(site, site_invitation) do
|
def invite_sync(site, site_invitation) do
|
||||||
site = Teams.load_for_site(site)
|
site = Teams.load_for_site(site)
|
||||||
site_invitation = Repo.preload(site_invitation, :inviter)
|
site_invitation = Repo.preload(site_invitation, :inviter)
|
||||||
@ -97,31 +66,6 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def transfer_site(site, new_owner, now \\ NaiveDateTime.utc_now(:second)) do
|
|
||||||
site = Repo.preload(site, :team)
|
|
||||||
|
|
||||||
with :ok <- ensure_transfer_valid(site.team, new_owner),
|
|
||||||
{:ok, team} <- Teams.get_or_create(new_owner),
|
|
||||||
:ok <- ensure_can_take_ownership(site, team) do
|
|
||||||
site =
|
|
||||||
Repo.preload(site, [
|
|
||||||
:team,
|
|
||||||
:owner,
|
|
||||||
guest_memberships: [team_membership: :user],
|
|
||||||
guest_invitations: :team_invitation
|
|
||||||
])
|
|
||||||
|
|
||||||
{:ok, _} =
|
|
||||||
Repo.transaction(fn ->
|
|
||||||
:ok = transfer_site_ownership(site, team, now)
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:ok, team_membership} = Teams.Memberships.get(team, new_owner)
|
|
||||||
|
|
||||||
{:ok, team_membership}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def transfer_site_sync(site, user) do
|
def transfer_site_sync(site, user) do
|
||||||
{:ok, team} = Teams.get_or_create(user)
|
{:ok, team} = Teams.get_or_create(user)
|
||||||
site = Teams.load_for_site(site)
|
site = Teams.load_for_site(site)
|
||||||
@ -219,26 +163,29 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_transfer_permissions(_team, _initiator, false = _check_permissions?) do
|
def check_transfer_permissions(_team, _initiator, false = _check_permissions?) do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_transfer_permissions(team, initiator, _) do
|
def check_transfer_permissions(team, initiator, _) do
|
||||||
case Teams.Memberships.team_role(team, initiator) do
|
case Teams.Memberships.team_role(team, initiator) do
|
||||||
{:ok, :owner} -> :ok
|
{:ok, :owner} -> :ok
|
||||||
_ -> {:error, :forbidden}
|
_ -> {:error, :forbidden}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_transfer_valid(_team, nil), do: :ok
|
@doc false
|
||||||
|
def ensure_transfer_valid(_team, nil, :owner), do: :ok
|
||||||
|
|
||||||
defp ensure_transfer_valid(team, new_owner) do
|
def ensure_transfer_valid(team, new_owner, :owner) do
|
||||||
case Teams.Memberships.team_role(team, new_owner) do
|
case Teams.Memberships.team_role(team, new_owner) do
|
||||||
{:ok, :owner} -> {:error, :transfer_to_self}
|
{:ok, :owner} -> {:error, :transfer_to_self}
|
||||||
_ -> :ok
|
_ -> :ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_transfer_valid(_team, _new_owner, _role), do: :ok
|
||||||
|
|
||||||
defp create_site_transfer(site, initiator, invitee_email, now \\ NaiveDateTime.utc_now(:second)) do
|
defp create_site_transfer(site, initiator, invitee_email, now \\ NaiveDateTime.utc_now(:second)) do
|
||||||
site
|
site
|
||||||
|> Teams.SiteTransfer.changeset(initiator: initiator, email: invitee_email)
|
|> Teams.SiteTransfer.changeset(initiator: initiator, email: invitee_email)
|
||||||
@ -249,7 +196,7 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_transfer_init_email(site_transfer, new_owner) do
|
def send_transfer_init_email(site_transfer, new_owner) do
|
||||||
email =
|
email =
|
||||||
PlausibleWeb.Email.ownership_transfer_request(
|
PlausibleWeb.Email.ownership_transfer_request(
|
||||||
site_transfer.email,
|
site_transfer.email,
|
||||||
@ -296,7 +243,7 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
# - remove old guest memberships
|
# - remove old guest memberships
|
||||||
site_transfer = Repo.preload(site_transfer, [:initiator, site: :team])
|
site_transfer = Repo.preload(site_transfer, [:initiator, site: :team])
|
||||||
|
|
||||||
with :ok <- ensure_transfer_valid(site_transfer.site.team, new_owner),
|
with :ok <- ensure_transfer_valid(site_transfer.site.team, new_owner, :owner),
|
||||||
{:ok, team} <- Teams.get_or_create(new_owner),
|
{:ok, team} <- Teams.get_or_create(new_owner),
|
||||||
:ok <- ensure_can_take_ownership(site_transfer.site, team) do
|
:ok <- ensure_can_take_ownership(site_transfer.site, team) do
|
||||||
site = Repo.preload(site_transfer.site, guest_memberships: [team_membership: :user])
|
site = Repo.preload(site_transfer.site, guest_memberships: [team_membership: :user])
|
||||||
@ -465,21 +412,34 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_invitation_permissions(_team, _inviter, false = _check_permission?) do
|
@doc false
|
||||||
:ok
|
def check_invitation_permissions(site, inviter, invitation_role, opts) do
|
||||||
end
|
check_permissions? = Keyword.get(opts, :check_permissions, true)
|
||||||
|
|
||||||
defp check_invitation_permissions(team, inviter, _) do
|
if check_permissions? do
|
||||||
case Teams.Memberships.team_role(team, inviter) do
|
case Teams.Memberships.site_role(site, inviter) do
|
||||||
{:ok, role} when role in [:owner, :admin] -> :ok
|
{:ok, :owner} when invitation_role == :owner ->
|
||||||
_ -> {:error, :forbidden}
|
:ok
|
||||||
|
|
||||||
|
{:ok, inviter_role}
|
||||||
|
when inviter_role in [:owner, :editor, :admin] and invitation_role != :owner ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, :forbidden}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp translate_role(:admin), do: :editor
|
defp translate_role(:admin), do: :editor
|
||||||
defp translate_role(role), do: role
|
defp translate_role(role), do: role
|
||||||
|
|
||||||
defp check_team_member_limit(team, _role, invitee_email) do
|
@doc false
|
||||||
|
def check_team_member_limit(_team, :owner, _invitee_email), do: :ok
|
||||||
|
|
||||||
|
def check_team_member_limit(team, _role, invitee_email) do
|
||||||
limit = Teams.Billing.team_member_limit(team)
|
limit = Teams.Billing.team_member_limit(team)
|
||||||
usage = Teams.Billing.team_member_usage(team, exclude_emails: [invitee_email])
|
usage = Teams.Billing.team_member_usage(team, exclude_emails: [invitee_email])
|
||||||
|
|
||||||
@ -490,9 +450,12 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_new_membership(_site, nil, _role), do: :ok
|
@doc false
|
||||||
|
def ensure_new_membership(_site, nil, _role), do: :ok
|
||||||
|
|
||||||
defp ensure_new_membership(site, invitee, _role) do
|
def ensure_new_membership(_site, _invitee, :owner), do: :ok
|
||||||
|
|
||||||
|
def ensure_new_membership(site, invitee, _role) do
|
||||||
if Teams.Memberships.site_role(site, invitee) == {:error, :not_a_member} do
|
if Teams.Memberships.site_role(site, invitee) == {:error, :not_a_member} do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
@ -535,7 +498,21 @@ defmodule Plausible.Teams.Invitations do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp send_invitation_email(guest_invitation, invitee) do
|
@doc false
|
||||||
|
def send_invitation_email(%Teams.SiteTransfer{} = transfer, invitee) do
|
||||||
|
email =
|
||||||
|
PlausibleWeb.Email.ownership_transfer_request(
|
||||||
|
transfer.email,
|
||||||
|
transfer.transfer_id,
|
||||||
|
transfer.site,
|
||||||
|
transfer.initiator,
|
||||||
|
invitee
|
||||||
|
)
|
||||||
|
|
||||||
|
Plausible.Mailer.send(email)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_invitation_email(%Teams.GuestInvitation{} = guest_invitation, invitee) do
|
||||||
team_invitation = guest_invitation.team_invitation
|
team_invitation = guest_invitation.team_invitation
|
||||||
|
|
||||||
email =
|
email =
|
||||||
|
@ -174,7 +174,8 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
|
|||||||
paddle_product_id = get_paddle_product_id(assigns.plan_to_render, assigns.selected_interval)
|
paddle_product_id = get_paddle_product_id(assigns.plan_to_render, assigns.selected_interval)
|
||||||
change_plan_link_text = change_plan_link_text(assigns)
|
change_plan_link_text = change_plan_link_text(assigns)
|
||||||
|
|
||||||
subscription = assigns.current_user.subscription
|
subscription =
|
||||||
|
Plausible.Teams.Adapter.Read.Billing.get_subscription(assigns.current_user)
|
||||||
|
|
||||||
billing_details_expired =
|
billing_details_expired =
|
||||||
Subscription.Status.in?(subscription, [
|
Subscription.Status.in?(subscription, [
|
||||||
@ -270,15 +271,15 @@ defmodule PlausibleWeb.Components.Billing.PlanBox do
|
|||||||
# because in the past we've let users upgrade without that constraint, as
|
# because in the past we've let users upgrade without that constraint, as
|
||||||
# well as transfer sites to those accounts. to these accounts we won't be
|
# well as transfer sites to those accounts. to these accounts we won't be
|
||||||
# offering an extra pageview limit allowance margin though.
|
# offering an extra pageview limit allowance margin though.
|
||||||
invited_user? = is_nil(current_user.trial_expiry_date)
|
invited_user? = is_nil(Plausible.Teams.Adapter.Read.Teams.trial_expiry_date(current_user))
|
||||||
|
|
||||||
trial_active_or_ended_recently? =
|
trial_active_or_ended_recently? =
|
||||||
not invited_user? &&
|
not invited_user? &&
|
||||||
Date.diff(Date.utc_today(), current_user.trial_expiry_date) <= 10
|
Plausible.Teams.Adapter.Read.Teams.trial_days_left(current_user) >= -10
|
||||||
|
|
||||||
limit_checking_opts =
|
limit_checking_opts =
|
||||||
cond do
|
cond do
|
||||||
current_user.allow_next_upgrade_override ->
|
Plausible.Teams.Adapter.Read.Billing.allow_next_upgrade_override?(current_user) ->
|
||||||
[ignore_pageview_limit: true]
|
[ignore_pageview_limit: true]
|
||||||
|
|
||||||
trial_active_or_ended_recently? && plan.volume == "10k" ->
|
trial_active_or_ended_recently? && plan.volume == "10k" ->
|
||||||
|
@ -29,8 +29,8 @@ defmodule PlausibleWeb.Site.MembershipController do
|
|||||||
|> Plausible.Teams.Adapter.Read.Sites.get_for_user!(conn.assigns.site.domain)
|
|> Plausible.Teams.Adapter.Read.Sites.get_for_user!(conn.assigns.site.domain)
|
||||||
|> Plausible.Repo.preload(:owner)
|
|> Plausible.Repo.preload(:owner)
|
||||||
|
|
||||||
limit = Plausible.Billing.Quota.Limits.team_member_limit(site.owner)
|
limit = Plausible.Teams.Adapter.Read.Billing.team_member_limit(site.owner)
|
||||||
usage = Plausible.Billing.Quota.Usage.team_member_usage(site.owner)
|
usage = Plausible.Teams.Adapter.Read.Billing.team_member_usage(site.owner)
|
||||||
below_limit? = Plausible.Billing.Quota.below_limit?(usage, limit)
|
below_limit? = Plausible.Billing.Quota.below_limit?(usage, limit)
|
||||||
|
|
||||||
render(
|
render(
|
||||||
|
@ -25,7 +25,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
|
|||||||
current_user: current_user,
|
current_user: current_user,
|
||||||
pending_ownership_site_ids: pending_ownership_site_ids
|
pending_ownership_site_ids: pending_ownership_site_ids
|
||||||
} ->
|
} ->
|
||||||
Quota.Usage.usage(current_user,
|
Plausible.Teams.Adapter.Read.Billing.quota_usage(current_user,
|
||||||
with_features: true,
|
with_features: true,
|
||||||
pending_ownership_site_ids: pending_ownership_site_ids
|
pending_ownership_site_ids: pending_ownership_site_ids
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
defmodule Plausible.Site.AdminTest do
|
defmodule Plausible.Site.AdminTest do
|
||||||
use Plausible
|
use Plausible
|
||||||
use Plausible.DataCase, async: true
|
use Plausible.DataCase, async: true
|
||||||
|
use Plausible.Teams.Test
|
||||||
use Bamboo.Test
|
use Bamboo.Test
|
||||||
|
|
||||||
@subject_prefix if ee?(), do: "[Plausible Analytics] ", else: "[Plausible CE] "
|
@subject_prefix if ee?(), do: "[Plausible Analytics] ", else: "[Plausible CE] "
|
||||||
@ -38,9 +39,8 @@ defmodule Plausible.Site.AdminTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "new owner can't be the same as old owner", %{conn: conn, transfer_action: action} do
|
test "new owner can't be the same as old owner", %{conn: conn, transfer_action: action} do
|
||||||
current_owner = insert(:user)
|
current_owner = new_user()
|
||||||
|
site = new_site(owner: current_owner)
|
||||||
site = insert(:site, members: [current_owner])
|
|
||||||
|
|
||||||
assert {:error, "User is already an owner of one of the sites"} =
|
assert {:error, "User is already an owner of one of the sites"} =
|
||||||
action.(conn, [site], %{"email" => current_owner.email})
|
action.(conn, [site], %{"email" => current_owner.email})
|
||||||
@ -50,14 +50,10 @@ defmodule Plausible.Site.AdminTest do
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
transfer_action: action
|
transfer_action: action
|
||||||
} do
|
} do
|
||||||
current_owner = insert(:user)
|
current_owner = new_user()
|
||||||
new_owner = insert(:user)
|
new_owner = new_user()
|
||||||
|
site1 = new_site(owner: current_owner)
|
||||||
site1 =
|
site2 = new_site(owner: current_owner)
|
||||||
insert(:site, memberships: [build(:site_membership, user: current_owner, role: :owner)])
|
|
||||||
|
|
||||||
site2 =
|
|
||||||
insert(:site, memberships: [build(:site_membership, user: current_owner, role: :owner)])
|
|
||||||
|
|
||||||
assert :ok = action.(conn, [site1, site2], %{"email" => new_owner.email})
|
assert :ok = action.(conn, [site1, site2], %{"email" => new_owner.email})
|
||||||
|
|
||||||
|
@ -78,64 +78,6 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
|
|||||||
assert_guest_membership(team, site, existing_owner, :editor)
|
assert_guest_membership(team, site, existing_owner, :editor)
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag :teams
|
|
||||||
test "transfers ownership successfully (TEAM)" do
|
|
||||||
site = insert(:site, memberships: [])
|
|
||||||
existing_owner = insert(:user)
|
|
||||||
|
|
||||||
_existing_membership =
|
|
||||||
insert(:site_membership, user: existing_owner, site: site, role: :owner)
|
|
||||||
|
|
||||||
site = Plausible.Teams.load_for_site(site)
|
|
||||||
old_team = site.team
|
|
||||||
|
|
||||||
another_user = insert(:user)
|
|
||||||
|
|
||||||
another_team_membership =
|
|
||||||
insert(:team_membership, user: another_user, team: old_team, role: :guest)
|
|
||||||
|
|
||||||
another_guest_membership =
|
|
||||||
insert(:guest_membership,
|
|
||||||
team_membership: another_team_membership,
|
|
||||||
site: site,
|
|
||||||
role: :viewer
|
|
||||||
)
|
|
||||||
|
|
||||||
new_owner = insert(:user)
|
|
||||||
new_team = insert(:team)
|
|
||||||
insert(:team_membership, user: new_owner, team: new_team, role: :owner)
|
|
||||||
insert(:growth_subscription, user: new_owner, team: new_team)
|
|
||||||
|
|
||||||
assert {:ok, new_team_membership} =
|
|
||||||
Plausible.Teams.Invitations.transfer_site(site, new_owner)
|
|
||||||
|
|
||||||
assert new_team_membership.team_id == new_team.id
|
|
||||||
assert new_team_membership.user_id == new_owner.id
|
|
||||||
assert new_team_membership.role == :owner
|
|
||||||
|
|
||||||
assert_team_membership(existing_owner, old_team)
|
|
||||||
|
|
||||||
refute Repo.reload(another_team_membership)
|
|
||||||
refute Repo.reload(another_guest_membership)
|
|
||||||
|
|
||||||
assert new_another_team_membership =
|
|
||||||
Plausible.Teams.Membership
|
|
||||||
|> Repo.get_by(
|
|
||||||
team_id: new_team.id,
|
|
||||||
user_id: another_user.id
|
|
||||||
)
|
|
||||||
|> Repo.preload(:guest_memberships)
|
|
||||||
|
|
||||||
assert another_team_membership.id != new_another_team_membership.id
|
|
||||||
assert [new_another_guest_membership] = new_another_team_membership.guest_memberships
|
|
||||||
assert new_another_guest_membership.site_id == site.id
|
|
||||||
assert new_another_guest_membership.role == another_guest_membership.role
|
|
||||||
|
|
||||||
assert new_another_team_membership.role == :guest
|
|
||||||
|
|
||||||
assert_no_emails_delivered()
|
|
||||||
end
|
|
||||||
|
|
||||||
@tag :ee_only
|
@tag :ee_only
|
||||||
test "unlocks the site if it was previously locked" do
|
test "unlocks the site if it was previously locked" do
|
||||||
site = insert(:site, locked: true, memberships: [])
|
site = insert(:site, locked: true, memberships: [])
|
||||||
|
@ -9,137 +9,38 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
|
|
||||||
describe "create_invitation/4" do
|
describe "create_invitation/4" do
|
||||||
test "creates an invitation" do
|
test "creates an invitation" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
invitee = insert(:user)
|
invitee = new_user()
|
||||||
team = insert(:team)
|
site = new_site(owner: inviter)
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: team,
|
|
||||||
memberships: [build(:site_membership, user: inviter, role: :owner)]
|
|
||||||
)
|
|
||||||
|
|
||||||
insert(:team_membership, team: team, user: inviter, role: :owner)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
||||||
|
|
||||||
assert {:ok, %Plausible.Teams.GuestInvitation{}} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, invitee.email, :viewer)
|
|
||||||
end
|
|
||||||
|
|
||||||
@tag :teams
|
|
||||||
test "[TEAMS] syncs a created invitation" do
|
|
||||||
inviter = insert(:user)
|
|
||||||
invitee = insert(:user)
|
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: nil,
|
|
||||||
memberships: [build(:site_membership, user: inviter, role: :owner)]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
|
||||||
|
|
||||||
team = assert_team_attached(site)
|
|
||||||
assert_guest_invitation(team, site, invitee.email, :viewer)
|
|
||||||
end
|
|
||||||
|
|
||||||
@tag :teams
|
|
||||||
test "[TEAMS] sync a created invitation with team already setup but site not assigned yet" do
|
|
||||||
inviter = insert(:user)
|
|
||||||
invitee = insert(:user)
|
|
||||||
|
|
||||||
{:ok, %{id: team_id}} = Plausible.Teams.get_or_create(inviter)
|
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: nil,
|
|
||||||
memberships: [build(:site_membership, user: inviter, role: :owner)]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
|
||||||
|
|
||||||
team = assert_team_attached(site, team_id)
|
|
||||||
assert_guest_invitation(team, site, invitee.email, :viewer)
|
|
||||||
end
|
|
||||||
|
|
||||||
@tag :teams
|
|
||||||
test "[TEAMS] sync a created invitation with team fully setup" do
|
|
||||||
inviter = insert(:user)
|
|
||||||
invitee = insert(:user)
|
|
||||||
|
|
||||||
{:ok, %{id: team_id} = team} = Plausible.Teams.get_or_create(inviter)
|
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: team,
|
|
||||||
memberships: [build(:site_membership, user: inviter, role: :owner)]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
|
||||||
|
|
||||||
team = assert_team_attached(site, team_id)
|
|
||||||
assert_guest_invitation(team, site, invitee.email, :viewer)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns validation errors" do
|
test "returns validation errors" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
team = insert(:team)
|
site = new_site(owner: inviter)
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: team,
|
|
||||||
memberships: [build(:site_membership, user: inviter, role: :owner)]
|
|
||||||
)
|
|
||||||
|
|
||||||
insert(:team_membership, team: team, user: inviter, role: :owner)
|
|
||||||
|
|
||||||
assert {:error, changeset} = CreateInvitation.create_invitation(site, inviter, "", :viewer)
|
assert {:error, changeset} = CreateInvitation.create_invitation(site, inviter, "", :viewer)
|
||||||
assert {"can't be blank", _} = changeset.errors[:email]
|
assert {"can't be blank", _} = changeset.errors[:email]
|
||||||
|
|
||||||
assert {:error, changeset} = Plausible.Teams.Invitations.invite(site, inviter, "", :viewer)
|
|
||||||
assert {"can't be blank", _} = changeset.errors[:email]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns error when user is already a member" do
|
test "returns error when user is already a member" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
invitee = insert(:user)
|
invitee = new_user()
|
||||||
|
site = new_site(owner: inviter)
|
||||||
team = insert(:team)
|
add_guest(site, user: invitee, role: :viewer)
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
team: team,
|
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: inviter, role: :owner),
|
|
||||||
build(:site_membership, user: invitee, role: :viewer)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
insert(:team_membership, team: team, user: inviter, role: :owner)
|
|
||||||
team_membership = insert(:team_membership, team: team, user: invitee, role: :guest)
|
|
||||||
insert(:guest_membership, team_membership: team_membership, site: site, role: :viewer)
|
|
||||||
|
|
||||||
assert {:error, :already_a_member} =
|
assert {:error, :already_a_member} =
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
||||||
|
|
||||||
assert {:error, :already_a_member} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, invitee.email, :viewer)
|
|
||||||
|
|
||||||
assert {:error, :already_a_member} =
|
assert {:error, :already_a_member} =
|
||||||
CreateInvitation.create_invitation(site, inviter, inviter.email, :viewer)
|
CreateInvitation.create_invitation(site, inviter, inviter.email, :viewer)
|
||||||
|
|
||||||
assert {:error, :already_a_member} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, inviter.email, :viewer)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "sends invitation email for existing users" do
|
test "sends invitation email for existing users" do
|
||||||
[inviter, invitee] = insert_list(2, :user)
|
[inviter, invitee] = for _ <- 1..2, do: new_user()
|
||||||
site = insert(:site, memberships: [build(:site_membership, user: inviter, role: :owner)])
|
site = new_site(owner: inviter)
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
||||||
@ -151,8 +52,8 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "sends invitation email for new users" do
|
test "sends invitation email for new users" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
site = insert(:site, memberships: [build(:site_membership, user: inviter, role: :owner)])
|
site = new_site(owner: inviter)
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :viewer)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :viewer)
|
||||||
@ -165,15 +66,11 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
|
|
||||||
@tag :ee_only
|
@tag :ee_only
|
||||||
test "returns error when owner is over their team member limit" do
|
test "returns error when owner is over their team member limit" do
|
||||||
[owner, inviter, invitee] = insert_list(3, :user)
|
[owner, inviter, invitee] = for _ <- 1..3, do: new_user()
|
||||||
|
|
||||||
memberships =
|
site = new_site(owner: owner)
|
||||||
[
|
inviter = add_guest(site, user: inviter, role: :editor)
|
||||||
build(:site_membership, user: owner, role: :owner),
|
for _ <- 1..4, do: add_guest(site, role: :viewer)
|
||||||
build(:site_membership, user: inviter, role: :admin)
|
|
||||||
] ++ build_list(4, :site_membership)
|
|
||||||
|
|
||||||
site = insert(:site, memberships: memberships)
|
|
||||||
|
|
||||||
assert {:error, {:over_limit, 3}} =
|
assert {:error, {:over_limit, 3}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
CreateInvitation.create_invitation(site, inviter, invitee.email, :viewer)
|
||||||
@ -181,14 +78,8 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
|
|
||||||
@tag :ee_only
|
@tag :ee_only
|
||||||
test "allows inviting users who were already invited to other sites, within the limit" do
|
test "allows inviting users who were already invited to other sites, within the limit" do
|
||||||
owner = insert(:user)
|
owner = new_user()
|
||||||
|
site = new_site(owner: owner)
|
||||||
memberships =
|
|
||||||
[
|
|
||||||
build(:site_membership, user: owner, role: :owner)
|
|
||||||
]
|
|
||||||
|
|
||||||
site = insert(:site, memberships: memberships)
|
|
||||||
|
|
||||||
invite = fn site, email ->
|
invite = fn site, email ->
|
||||||
CreateInvitation.create_invitation(site, owner, email, :viewer)
|
CreateInvitation.create_invitation(site, owner, email, :viewer)
|
||||||
@ -199,28 +90,24 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
assert {:ok, _} = invite.(site, "i3@example.com")
|
assert {:ok, _} = invite.(site, "i3@example.com")
|
||||||
assert {:error, {:over_limit, 3}} = invite.(site, "i4@example.com")
|
assert {:error, {:over_limit, 3}} = invite.(site, "i4@example.com")
|
||||||
|
|
||||||
site2 = insert(:site, memberships: memberships)
|
site2 = new_site(owner: owner)
|
||||||
|
|
||||||
assert {:ok, _} = invite.(site2, "i3@example.com")
|
assert {:ok, _} = invite.(site2, "i3@example.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
@tag :ee_only
|
@tag :ee_only
|
||||||
test "allows inviting users who are already members of other sites, within the limit" do
|
test "allows inviting users who are already members of other sites, within the limit" do
|
||||||
[u1, u2, u3, u4] = insert_list(4, :user)
|
[u1, u2, u3, u4] = for _ <- 1..4, do: new_user()
|
||||||
|
site = new_site(owner: u1)
|
||||||
|
add_guest(site, user: u2, role: :viewer)
|
||||||
|
add_guest(site, user: u3, role: :viewer)
|
||||||
|
add_guest(site, user: u4, role: :viewer)
|
||||||
|
|
||||||
memberships =
|
site2 = new_site(owner: u1)
|
||||||
[
|
add_guest(site2, user: u2, role: :viewer)
|
||||||
build(:site_membership, user: u1, role: :owner),
|
add_guest(site2, user: u3, role: :viewer)
|
||||||
build(:site_membership, user: u2, role: :viewer),
|
|
||||||
build(:site_membership, user: u3, role: :viewer)
|
|
||||||
]
|
|
||||||
|
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
memberships: memberships ++ [build(:site_membership, user: u4, role: :viewer)]
|
|
||||||
)
|
|
||||||
|
|
||||||
site2 = insert(:site, memberships: memberships)
|
|
||||||
|
|
||||||
invite = fn site, email ->
|
invite = fn site, email ->
|
||||||
CreateInvitation.create_invitation(site, u1, email, :viewer)
|
CreateInvitation.create_invitation(site, u1, email, :viewer)
|
||||||
@ -232,8 +119,8 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "sends ownership transfer email when invitation role is owner" do
|
test "sends ownership transfer email when invitation role is owner" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
site = insert(:site, memberships: [build(:site_membership, user: inviter, role: :owner)])
|
site = new_site(owner: inviter)
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
||||||
@ -245,68 +132,37 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "only allows owners to transfer ownership" do
|
test "only allows owners to transfer ownership" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
|
|
||||||
site =
|
site = new_site()
|
||||||
insert(:site,
|
add_guest(site, user: inviter, role: :editor)
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: build(:user), role: :owner),
|
|
||||||
build(:site_membership, user: inviter, role: :admin)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert {:error, :forbidden} =
|
assert {:error, :forbidden} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "allows ownership transfer to existing site members" do
|
test "allows ownership transfer to existing site members" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
invitee = insert(:user)
|
invitee = new_user()
|
||||||
|
site = new_site(owner: inviter)
|
||||||
site =
|
add_guest(site, user: invitee, role: :viewer)
|
||||||
insert(:site,
|
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: inviter, role: :owner),
|
|
||||||
build(:site_membership, user: invitee, role: :viewer)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> Plausible.Teams.load_for_site()
|
|
||||||
|
|
||||||
insert(:team_membership, team: site.team, user: invitee, role: :viewer)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, invitee.email, :owner)
|
CreateInvitation.create_invitation(site, inviter, invitee.email, :owner)
|
||||||
|
|
||||||
assert {:ok, %Plausible.Teams.SiteTransfer{}} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, invitee.email, :owner)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not allow transferring ownership to existing owner" do
|
test "does not allow transferring ownership to existing owner" do
|
||||||
inviter = insert(:user, email: "vini@plausible.test")
|
inviter = new_user(email: "vini@plausible.test")
|
||||||
|
site = new_site(owner: inviter)
|
||||||
site =
|
|
||||||
insert(:site,
|
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: inviter, role: :owner)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
site = Plausible.Teams.load_for_site(site)
|
|
||||||
|
|
||||||
assert {:error, :transfer_to_self} =
|
assert {:error, :transfer_to_self} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :owner)
|
||||||
|
|
||||||
assert {:error, :transfer_to_self} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, "vini@plausible.test", :owner)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "allows creating an ownership transfer even when at team member limit" do
|
test "allows creating an ownership transfer even when at team member limit" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
|
site = new_site(owner: inviter)
|
||||||
memberships =
|
for _ <- 1..3, do: add_guest(site, role: :viewer)
|
||||||
[build(:site_membership, user: inviter, role: :owner)] ++ build_list(3, :site_membership)
|
|
||||||
|
|
||||||
site = insert(:site, memberships: memberships)
|
|
||||||
|
|
||||||
assert {:ok, _invitation} =
|
assert {:ok, _invitation} =
|
||||||
CreateInvitation.create_invitation(
|
CreateInvitation.create_invitation(
|
||||||
@ -318,37 +174,19 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "does not allow viewers to invite users" do
|
test "does not allow viewers to invite users" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
owner = insert(:user)
|
owner = new_user()
|
||||||
|
site = new_site(owner: owner)
|
||||||
site =
|
add_guest(site, user: inviter, role: :viewer)
|
||||||
insert(:site,
|
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: owner, role: :owner),
|
|
||||||
build(:site_membership, user: inviter, role: :viewer)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> Plausible.Teams.load_for_site()
|
|
||||||
|
|
||||||
insert(:team_membership, team: site.team, user: inviter, role: :viewer)
|
|
||||||
|
|
||||||
assert {:error, :forbidden} =
|
assert {:error, :forbidden} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :viewer)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :viewer)
|
||||||
|
|
||||||
assert {:error, :forbidden} =
|
|
||||||
Plausible.Teams.Invitations.invite(site, inviter, "vini@plausible.test", :viewer)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "allows admins to invite other admins" do
|
test "allows admins to invite other admins" do
|
||||||
inviter = insert(:user)
|
inviter = new_user()
|
||||||
|
site = new_site()
|
||||||
site =
|
add_guest(site, user: inviter, role: :editor)
|
||||||
insert(:site,
|
|
||||||
memberships: [
|
|
||||||
build(:site_membership, user: build(:user), role: :owner),
|
|
||||||
build(:site_membership, user: inviter, role: :admin)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert {:ok, %Plausible.Auth.Invitation{}} =
|
assert {:ok, %Plausible.Auth.Invitation{}} =
|
||||||
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :admin)
|
CreateInvitation.create_invitation(site, inviter, "vini@plausible.test", :admin)
|
||||||
@ -357,14 +195,11 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
|
|
||||||
describe "bulk_create_invitation/5" do
|
describe "bulk_create_invitation/5" do
|
||||||
test "initiates ownership transfer for multiple sites in one action" do
|
test "initiates ownership transfer for multiple sites in one action" do
|
||||||
admin_user = insert(:user)
|
admin_user = new_user()
|
||||||
new_owner = insert(:user)
|
new_owner = new_user()
|
||||||
|
|
||||||
site1 =
|
site1 = new_site(owner: admin_user)
|
||||||
insert(:site, memberships: [build(:site_membership, user: admin_user, role: :owner)])
|
site2 = new_site(owner: admin_user)
|
||||||
|
|
||||||
site2 =
|
|
||||||
insert(:site, memberships: [build(:site_membership, user: admin_user, role: :owner)])
|
|
||||||
|
|
||||||
assert {:ok, _} =
|
assert {:ok, _} =
|
||||||
CreateInvitation.bulk_create_invitation(
|
CreateInvitation.bulk_create_invitation(
|
||||||
@ -397,11 +232,11 @@ defmodule Plausible.Site.Memberships.CreateInvitationTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "initiates ownership transfer for multiple sites in one action skipping permission checks" do
|
test "initiates ownership transfer for multiple sites in one action skipping permission checks" do
|
||||||
superadmin_user = insert(:user)
|
superadmin_user = new_user()
|
||||||
new_owner = insert(:user)
|
new_owner = new_user()
|
||||||
|
|
||||||
site1 = insert(:site)
|
site1 = new_site()
|
||||||
site2 = insert(:site)
|
site2 = new_site()
|
||||||
|
|
||||||
assert {:ok, _} =
|
assert {:ok, _} =
|
||||||
CreateInvitation.bulk_create_invitation(
|
CreateInvitation.bulk_create_invitation(
|
||||||
|
@ -293,9 +293,6 @@ defmodule PlausibleWeb.Site.MembershipControllerTest do
|
|||||||
put(conn, "/sites/#{site.domain}/memberships/u/#{collaborator.id}/role/viewer")
|
put(conn, "/sites/#{site.domain}/memberships/u/#{collaborator.id}/role/viewer")
|
||||||
|
|
||||||
assert_team_membership(collaborator, site.team, :viewer)
|
assert_team_membership(collaborator, site.team, :viewer)
|
||||||
|
|
||||||
old_model_membership = Repo.get_by(Plausible.Site.Membership, user_id: collaborator.id)
|
|
||||||
assert old_model_membership.role == :viewer
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag :teams
|
@tag :teams
|
||||||
|
@ -280,8 +280,8 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
insert_list(9, :site, members: [user])
|
for _ <- 1..9, do: new_site(owner: user)
|
||||||
assert 10 = Plausible.Billing.Quota.Usage.site_usage(user)
|
assert 10 = Plausible.Teams.Adapter.Read.Billing.site_usage(user)
|
||||||
|
|
||||||
another_user = new_user()
|
another_user = new_user()
|
||||||
pending_ownership_site = new_site(owner: another_user)
|
pending_ownership_site = new_site(owner: another_user)
|
||||||
@ -323,6 +323,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
user
|
user
|
||||||
|> Plausible.Auth.User.changeset(%{trial_expiry_date: Timex.shift(Timex.today(), days: -10)})
|
|> Plausible.Auth.User.changeset(%{trial_expiry_date: Timex.shift(Timex.today(), days: -10)})
|
||||||
|> Repo.update!()
|
|> Repo.update!()
|
||||||
|
|> Plausible.Teams.sync_team()
|
||||||
|
|
||||||
generate_usage_for(site, 13_000)
|
generate_usage_for(site, 13_000)
|
||||||
|
|
||||||
@ -349,6 +350,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
user
|
user
|
||||||
|> Plausible.Auth.User.changeset(%{trial_expiry_date: Timex.shift(Timex.today(), days: -11)})
|
|> Plausible.Auth.User.changeset(%{trial_expiry_date: Timex.shift(Timex.today(), days: -11)})
|
||||||
|> Repo.update!()
|
|> Repo.update!()
|
||||||
|
|> Plausible.Teams.sync_team()
|
||||||
|
|
||||||
generate_usage_for(site, 11_000)
|
generate_usage_for(site, 11_000)
|
||||||
|
|
||||||
@ -613,7 +615,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
new_site(owner: user)
|
new_site(owner: user)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert 50 = Plausible.Billing.Quota.Usage.site_usage(user)
|
assert 50 = Plausible.Teams.Adapter.Read.Billing.quota_usage(user).sites
|
||||||
|
|
||||||
another_user = new_user()
|
another_user = new_user()
|
||||||
pending_ownership_site = new_site(owner: another_user)
|
pending_ownership_site = new_site(owner: another_user)
|
||||||
@ -655,15 +657,8 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
insert(:site,
|
site = new_site(owner: user)
|
||||||
memberships: [
|
for _ <- 1..4, do: add_guest(site, role: :viewer)
|
||||||
build(:site_membership, user: user, role: :owner),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
{:ok, _lv, doc} = get_liveview(conn)
|
{:ok, _lv, doc} = get_liveview(conn)
|
||||||
|
|
||||||
@ -678,7 +673,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
for _ <- 1..11, do: insert(:site, members: [user])
|
for _ <- 1..11, do: new_site(owner: user)
|
||||||
|
|
||||||
{:ok, _lv, doc} = get_liveview(conn)
|
{:ok, _lv, doc} = get_liveview(conn)
|
||||||
|
|
||||||
@ -693,17 +688,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
for _ <- 1..11, do: insert(:site, members: [user])
|
for _ <- 1..11, do: new_site(owner: user)
|
||||||
|
|
||||||
insert(:site,
|
site = new_site(owner: user)
|
||||||
memberships: [
|
for _ <- 1..4, do: add_guest(site, role: :viewer)
|
||||||
build(:site_membership, user: user, role: :owner),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user)),
|
|
||||||
build(:site_membership, user: build(:user))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
{:ok, _lv, doc} = get_liveview(conn)
|
{:ok, _lv, doc} = get_liveview(conn)
|
||||||
|
|
||||||
@ -1111,7 +1099,10 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do
|
|||||||
|
|
||||||
defp create_subscription_for(user, subscription_opts) do
|
defp create_subscription_for(user, subscription_opts) do
|
||||||
{paddle_plan_id, subscription_opts} = Keyword.pop(subscription_opts, :paddle_plan_id)
|
{paddle_plan_id, subscription_opts} = Keyword.pop(subscription_opts, :paddle_plan_id)
|
||||||
user = subscribe_to_plan(user, paddle_plan_id, subscription_opts)
|
|
||||||
|
user =
|
||||||
|
subscribe_to_plan(user, paddle_plan_id, subscription_opts)
|
||||||
|
|
||||||
{:ok, user: user}
|
{:ok, user: user}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,8 +62,15 @@ defmodule Plausible.Teams.Test do
|
|||||||
|
|
||||||
insert(:site_membership, user: user, role: translate_role_to_old_model(role), site: site)
|
insert(:site_membership, user: user, role: translate_role_to_old_model(role), site: site)
|
||||||
|
|
||||||
team_membership = insert(:team_membership, team: team, user: user, role: :guest)
|
team_membership =
|
||||||
insert(:guest_membership, team_membership: team_membership, site: site, role: role)
|
build(:team_membership, team: team, user: user, role: :guest)
|
||||||
|
|> Repo.insert!(
|
||||||
|
on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]],
|
||||||
|
conflict_target: [:team_id, :user_id],
|
||||||
|
returning: true
|
||||||
|
)
|
||||||
|
|
||||||
|
insert(:guest_membership, site: site, team_membership: team_membership, role: role)
|
||||||
|
|
||||||
user |> Repo.preload([:site_memberships, :team_memberships])
|
user |> Repo.preload([:site_memberships, :team_memberships])
|
||||||
end
|
end
|
||||||
@ -152,7 +159,10 @@ defmodule Plausible.Teams.Test do
|
|||||||
def subscribe_to_plan(user, paddle_plan_id, attrs \\ []) do
|
def subscribe_to_plan(user, paddle_plan_id, attrs \\ []) do
|
||||||
{:ok, team} = Teams.get_or_create(user)
|
{:ok, team} = Teams.get_or_create(user)
|
||||||
attrs = Keyword.merge([user: user, team: team, paddle_plan_id: paddle_plan_id], attrs)
|
attrs = Keyword.merge([user: user, team: team, paddle_plan_id: paddle_plan_id], attrs)
|
||||||
subscription = insert(:subscription, attrs)
|
|
||||||
|
subscription =
|
||||||
|
insert(:subscription, attrs)
|
||||||
|
|
||||||
%{user | subscription: subscription}
|
%{user | subscription: subscription}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user