Apply feature gates to dashboard queries (#3424)

* Read feature status from Billing.Feature instead of %Site{}

This commit changes data attributes passed to React. Previously the
controller read feature statuses directly from the %Site{} schema. The
Billing.Feature context is aware of the user plan and the features
available.

* Limit funnels internal API based on site owner plan

* Limit props internal API based on site owner plan

* Use site factory in QueryTest

* Limit custom property filter based on site owner plan

* Limit revenue goals queries based on site owner plan
This commit is contained in:
Vini Brasil 2023-10-17 10:00:00 -03:00 committed by GitHub
parent 9b912f3d89
commit 896d78d8fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 230 additions and 85 deletions

View File

@ -94,8 +94,10 @@ function DropdownContent({ history, site, query, wrapped }) {
const [addingFilter, setAddingFilter] = useState(false);
if (wrapped === 0 || addingFilter) {
return Object.keys(FILTER_GROUPS)
.map((option) => filterDropdownOption(site, option))
let filterGroups = {...FILTER_GROUPS}
if (!site.propsEnabled) delete filterGroups.props
return Object.keys(filterGroups).map((option) => filterDropdownOption(site, option))
}
return (

View File

@ -19,6 +19,7 @@ defmodule Plausible.Stats.Query do
|> put_parsed_filters(params)
|> put_imported_opts(site, params)
|> put_sample_threshold(params)
|> maybe_drop_prop_filter(site)
end
defp query_by_period(site, %{"period" => "realtime"}) do
@ -228,6 +229,19 @@ defmodule Plausible.Stats.Query do
|> Map.put(:include_imported, include_imported?(query, site, requested?))
end
defp maybe_drop_prop_filter(query, site) do
prop_filter? = Map.has_key?(query.filters, "props")
props_available? = fn ->
site = Plausible.Repo.preload(site, :owner)
Plausible.Billing.Feature.Props.check_availability(site.owner) == :ok
end
if prop_filter? && !props_available?.(),
do: %__MODULE__{query | filters: Map.drop(query.filters, ["props"])},
else: query
end
@spec include_imported?(t(), Plausible.Site.t(), boolean()) :: boolean()
def include_imported?(query, site, requested?) do
cond do

View File

@ -29,8 +29,8 @@ defmodule Plausible.Stats.Util do
{atom() | nil, [atom()]}
@doc """
Returns the common currency for the goal filters in a query. If there are no
goal filters, or multiple currencies, `nil` is returned and revenue metrics
are dropped.
goal filters, multiple currencies or the site owner does not have access to
revenue goals, `nil` is returned and revenue metrics are dropped.
Aggregating revenue data works only for same currency goals. If the query is
filtered by goals with different currencies, for example, one USD and other
@ -44,7 +44,15 @@ defmodule Plausible.Stats.Util do
_any -> []
end
if Enum.any?(metrics, &(&1 in @revenue_metrics)) && Enum.any?(goal_filters) do
requested_revenue_metrics? = Enum.any?(metrics, &(&1 in @revenue_metrics))
filtering_by_goal? = Enum.any?(goal_filters)
revenue_goals_available? = fn ->
site = Plausible.Repo.preload(site, :owner)
Plausible.Billing.Feature.RevenueGoals.check_availability(site.owner) == :ok
end
if requested_revenue_metrics? && filtering_by_goal? && revenue_goals_available?.() do
revenue_goals_currencies =
Plausible.Repo.all(
from rg in Ecto.assoc(site, :revenue_goals),

View File

@ -4,6 +4,7 @@ defmodule PlausibleWeb.Api.StatsController do
use Plug.ErrorHandler
alias Plausible.Stats
alias Plausible.Stats.{Query, Filters, Comparisons}
alias PlausibleWeb.Api.Helpers, as: H
require Logger
@ -513,9 +514,10 @@ defmodule PlausibleWeb.Api.StatsController do
end
def funnel(conn, %{"id" => funnel_id} = params) do
site = conn.assigns[:site]
site = Plausible.Repo.preload(conn.assigns.site, :owner)
with :ok <- validate_params(site, params),
with :ok <- Plausible.Billing.Feature.Funnels.check_availability(site.owner),
:ok <- validate_params(site, params),
query <- Query.from(site, params) |> Filters.add_prefix(),
:ok <- validate_funnel_query(query),
{funnel_id, ""} <- Integer.parse(funnel_id),
@ -537,6 +539,12 @@ defmodule PlausibleWeb.Api.StatsController do
|> json(%{error: "Funnel not found"})
|> halt()
{:error, :upgrade_required} ->
H.payment_required(
conn,
"#{Plausible.Billing.Feature.Funnels.display_name()} is part of the Plausible Business plan. To get access to this feature, please upgrade your account."
)
_ ->
bad_request(conn, "There was an error with your request")
end
@ -1206,13 +1214,23 @@ defmodule PlausibleWeb.Api.StatsController do
end
def custom_prop_values(conn, params) do
site = conn.assigns[:site]
props = breakdown_custom_prop_values(site, params)
json(conn, props)
site = Plausible.Repo.preload(conn.assigns.site, :owner)
case Plausible.Billing.Feature.Props.check_availability(site.owner) do
:ok ->
props = breakdown_custom_prop_values(site, params)
json(conn, props)
{:error, :upgrade_required} ->
H.payment_required(
conn,
"#{Plausible.Billing.Feature.Props.display_name()} is part of the Plausible Business plan. To get access to this feature, please upgrade your account."
)
end
end
def all_custom_prop_values(conn, params) do
site = conn.assigns[:site]
site = conn.assigns.site
query = Query.from(site, params) |> Filters.add_prefix()
prop_names = Plausible.Stats.CustomProps.fetch_prop_names(site, query)

View File

@ -49,6 +49,7 @@ defmodule PlausibleWeb.StatsController do
plug(PlausibleWeb.AuthorizeSiteAccess when action in [:stats, :csv_export])
def stats(%{assigns: %{site: site}} = conn, _params) do
site = Plausible.Repo.preload(site, :owner)
stats_start_date = Plausible.Sites.stats_start_date(site)
can_see_stats? = not Sites.locked?(site) or conn.assigns[:current_user_role] == :super_admin
demo = site.domain == PlausibleWeb.Endpoint.host()
@ -95,7 +96,7 @@ defmodule PlausibleWeb.StatsController do
"""
def csv_export(conn, params) do
if is_nil(params["interval"]) or Plausible.Stats.Interval.valid?(params["interval"]) do
site = conn.assigns[:site]
site = Plausible.Repo.preload(conn.assigns.site, :owner)
query = Query.from(site, params) |> Filters.add_prefix()
metrics =
@ -144,10 +145,18 @@ defmodule PlausibleWeb.StatsController do
'operating_systems.csv' => fn -> Api.StatsController.operating_systems(conn, params) end,
'devices.csv' => fn -> Api.StatsController.screen_sizes(conn, params) end,
'conversions.csv' => fn -> Api.StatsController.conversions(conn, params) end,
'referrers.csv' => fn -> Api.StatsController.referrers(conn, params) end,
'custom_props.csv' => fn -> Api.StatsController.all_custom_prop_values(conn, params) end
'referrers.csv' => fn -> Api.StatsController.referrers(conn, params) end
}
csvs =
if Plausible.Billing.Feature.Props.enabled?(site) do
Map.put(csvs, 'custom_props.csv', fn ->
Api.StatsController.all_custom_prop_values(conn, params)
end)
else
csvs
end
csv_values =
Map.values(csvs)
|> Plausible.ClickhouseRepo.parallel_tasks()

View File

@ -18,9 +18,9 @@
data-domain="<%= @site.domain %>"
data-offset="<%= Plausible.Site.tz_offset(@site) %>"
data-has-goals="<%= @has_goals %>"
data-conversions-enabled="<%= @site.conversions_enabled %>"
data-funnels-enabled="<%= @site.funnels_enabled %>"
data-props-enabled="<%= @site.props_enabled %>"
data-conversions-enabled="<%= Plausible.Billing.Feature.Goals.enabled?(@site) %>"
data-funnels-enabled="<%= Plausible.Billing.Feature.Funnels.enabled?(@site) %>"
data-props-enabled="<%= Plausible.Billing.Feature.Props.enabled?(@site) %>"
data-funnels="<%= Jason.encode!(@funnels) %>"
data-has-props="<%= @has_props %>"
data-logged-in="<%= !!@conn.assigns[:current_user] %>"

View File

@ -12,7 +12,7 @@ defmodule Plausible.DebugReplayInfoTest do
end
test "adds replayable sentry context" do
site = build(:site)
site = insert(:site)
query = Plausible.Stats.Query.from(site, %{"period" => "day"})
{:ok, {^site, ^query}} = SampleModule.task(site, query, self())

View File

@ -5,7 +5,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with period set to this month" do
test "shifts back this month period when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-03-02"})
now = ~N[2023-03-02 14:00:00]
@ -16,7 +16,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back this month period when it's the first day of the month and mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-03-01"})
now = ~N[2023-03-01 14:00:00]
@ -27,7 +27,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "matches the day of the week when nearest day is original query start date and mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-03-02"})
now = ~N[2023-03-02 14:00:00]
@ -41,7 +41,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with period set to previous month" do
test "shifts back using the same number of days when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-02-01"})
now = ~N[2023-03-01 14:00:00]
@ -52,7 +52,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back the full month when mode is year_over_year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-02-01"})
now = ~N[2023-03-01 14:00:00]
@ -63,7 +63,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back whole month plus one day when mode is year_over_year and a leap year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2020-02-01"})
now = ~N[2023-03-01 14:00:00]
@ -74,7 +74,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "matches the day of the week when mode is previous_period keeping the same day" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-02-01"})
now = ~N[2023-03-01 14:00:00]
@ -86,7 +86,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "matches the day of the week when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "month", "date" => "2023-01-01"})
now = ~N[2023-03-01 14:00:00]
@ -100,7 +100,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with period set to year to date" do
test "shifts back by the same number of days when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "year", "date" => "2023-03-01"})
now = ~N[2023-03-01 14:00:00]
@ -111,7 +111,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back by the same number of days when mode is year_over_year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "year", "date" => "2023-03-01"})
now = ~N[2023-03-01 14:00:00]
@ -122,7 +122,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "matches the day of the week when mode is year_over_year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "year", "date" => "2023-03-01"})
now = ~N[2023-03-01 14:00:00]
@ -136,7 +136,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with period set to previous year" do
test "shifts back a whole year when mode is year_over_year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "year", "date" => "2022-03-02"})
{:ok, comparison} = Comparisons.compare(site, query, "year_over_year")
@ -146,7 +146,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back a whole year when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "year", "date" => "2022-03-02"})
{:ok, comparison} = Comparisons.compare(site, query, "previous_period")
@ -158,7 +158,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with period set to custom" do
test "shifts back by the same number of days when mode is previous_period" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "custom", "date" => "2023-01-01,2023-01-07"})
{:ok, comparison} = Comparisons.compare(site, query, "previous_period")
@ -168,7 +168,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "shifts back to last year when mode is year_over_year" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "custom", "date" => "2023-01-01,2023-01-07"})
{:ok, comparison} = Comparisons.compare(site, query, "year_over_year")
@ -180,7 +180,7 @@ defmodule Plausible.Stats.ComparisonsTest do
describe "with mode set to custom" do
test "sets first and last dates" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "custom", "date" => "2023-01-01,2023-01-07"})
{:ok, comparison} =
@ -191,7 +191,7 @@ defmodule Plausible.Stats.ComparisonsTest do
end
test "validates from and to dates" do
site = build(:site)
site = insert(:site)
query = Query.from(site, %{"period" => "custom", "date" => "2023-01-01,2023-01-07"})
assert {:error, :invalid_dates} ==

View File

@ -1,48 +1,56 @@
defmodule Plausible.Stats.QueryTest do
use ExUnit.Case, async: true
use Plausible.DataCase, async: true
alias Plausible.Stats.Query
@v4_growth_plan_id "change-me-749342"
@v4_business_plan_id "change-me-b749342"
@site_inserted_at ~D[2020-01-01]
@site %Plausible.Site{
timezone: "UTC",
inserted_at: @site_inserted_at,
stats_start_date: @site_inserted_at
}
setup do
user = insert(:user)
test "parses day format" do
q = Query.from(@site, %{"period" => "day", "date" => "2019-01-01"})
site =
insert(:site,
members: [user],
inserted_at: ~N[2020-01-01T00:00:00],
stats_start_date: ~D[2020-01-01]
)
{:ok, site: site, user: user}
end
test "parses day format", %{site: site} do
q = Query.from(site, %{"period" => "day", "date" => "2019-01-01"})
assert q.date_range.first == ~D[2019-01-01]
assert q.date_range.last == ~D[2019-01-01]
assert q.interval == "hour"
end
test "day format defaults to today" do
q = Query.from(@site, %{"period" => "day"})
test "day format defaults to today", %{site: site} do
q = Query.from(site, %{"period" => "day"})
assert q.date_range.first == Timex.today()
assert q.date_range.last == Timex.today()
assert q.interval == "hour"
end
test "parses realtime format" do
q = Query.from(@site, %{"period" => "realtime"})
test "parses realtime format", %{site: site} do
q = Query.from(site, %{"period" => "realtime"})
assert q.date_range.first == Timex.today()
assert q.date_range.last == Timex.today()
assert q.period == "realtime"
end
test "parses month format" do
q = Query.from(@site, %{"period" => "month", "date" => "2019-01-01"})
test "parses month format", %{site: site} do
q = Query.from(site, %{"period" => "month", "date" => "2019-01-01"})
assert q.date_range.first == ~D[2019-01-01]
assert q.date_range.last == ~D[2019-01-31]
assert q.interval == "date"
end
test "parses 6 month format" do
q = Query.from(@site, %{"period" => "6mo"})
test "parses 6 month format", %{site: site} do
q = Query.from(site, %{"period" => "6mo"})
assert q.date_range.first ==
Timex.shift(Timex.today(), months: -5) |> Timex.beginning_of_month()
@ -51,8 +59,8 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "month"
end
test "parses 12 month format" do
q = Query.from(@site, %{"period" => "12mo"})
test "parses 12 month format", %{site: site} do
q = Query.from(site, %{"period" => "12mo"})
assert q.date_range.first ==
Timex.shift(Timex.today(), months: -11) |> Timex.beginning_of_month()
@ -61,37 +69,37 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "month"
end
test "parses year to date format" do
q = Query.from(@site, %{"period" => "year"})
test "parses year to date format", %{site: site} do
q = Query.from(site, %{"period" => "year"})
assert q.date_range.first ==
Timex.now(@site.timezone) |> Timex.to_date() |> Timex.beginning_of_year()
Timex.now(site.timezone) |> Timex.to_date() |> Timex.beginning_of_year()
assert q.date_range.last ==
Timex.now(@site.timezone) |> Timex.to_date() |> Timex.end_of_year()
Timex.now(site.timezone) |> Timex.to_date() |> Timex.end_of_year()
assert q.interval == "month"
end
test "parses all time" do
q = Query.from(@site, %{"period" => "all"})
test "parses all time", %{site: site} do
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == @site_inserted_at
assert q.date_range.first == NaiveDateTime.to_date(site.inserted_at)
assert q.date_range.last == Timex.today()
assert q.period == "all"
assert q.interval == "month"
end
test "parses all time in correct timezone" do
site = Map.put(@site, :timezone, "America/Cancun")
test "parses all time in correct timezone", %{site: site} do
site = Map.put(site, :timezone, "America/Cancun")
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == ~D[2019-12-31]
assert q.date_range.last == Timex.today("America/Cancun")
end
test "all time shows today if site has no start date" do
site = Map.put(@site, :stats_start_date, nil)
test "all time shows today if site has no start date", %{site: site} do
site = Map.put(site, :stats_start_date, nil)
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == Timex.today()
@ -100,8 +108,8 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "hour"
end
test "all time shows hourly if site is completely new" do
site = Map.put(@site, :stats_start_date, Timex.now())
test "all time shows hourly if site is completely new", %{site: site} do
site = Map.put(site, :stats_start_date, Timex.now())
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == Timex.today()
@ -110,8 +118,8 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "hour"
end
test "all time shows daily if site is more than a day old" do
site = Map.put(@site, :stats_start_date, Timex.now() |> Timex.shift(days: -1))
test "all time shows daily if site is more than a day old", %{site: site} do
site = Map.put(site, :stats_start_date, Timex.now() |> Timex.shift(days: -1))
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == Timex.today() |> Timex.shift(days: -1)
@ -120,8 +128,8 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "date"
end
test "all time shows monthly if site is more than a month old" do
site = Map.put(@site, :stats_start_date, Timex.now() |> Timex.shift(months: -1))
test "all time shows monthly if site is more than a month old", %{site: site} do
site = Map.put(site, :stats_start_date, Timex.now() |> Timex.shift(months: -1))
q = Query.from(site, %{"period" => "all"})
assert q.date_range.first == Timex.today() |> Timex.shift(months: -1)
@ -130,8 +138,8 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "month"
end
test "all time uses passed interval different from the default interval" do
site = Map.put(@site, :stats_start_date, Timex.now() |> Timex.shift(months: -1))
test "all time uses passed interval different from the default interval", %{site: site} do
site = Map.put(site, :stats_start_date, Timex.now() |> Timex.shift(months: -1))
q = Query.from(site, %{"period" => "all", "interval" => "week"})
assert q.date_range.first == Timex.today() |> Timex.shift(months: -1)
@ -140,41 +148,57 @@ defmodule Plausible.Stats.QueryTest do
assert q.interval == "week"
end
test "defaults to 30 days format" do
assert Query.from(@site, %{}) == Query.from(@site, %{"period" => "30d"})
test "defaults to 30 days format", %{site: site} do
assert Query.from(site, %{}) == Query.from(site, %{"period" => "30d"})
end
test "parses custom format" do
q = Query.from(@site, %{"period" => "custom", "from" => "2019-01-01", "to" => "2019-01-15"})
test "parses custom format", %{site: site} do
q = Query.from(site, %{"period" => "custom", "from" => "2019-01-01", "to" => "2019-01-15"})
assert q.date_range.first == ~D[2019-01-01]
assert q.date_range.last == ~D[2019-01-15]
assert q.interval == "date"
end
test "adds sample_threshold :infinite to query struct" do
q = Query.from(@site, %{"period" => "30d", "sample_threshold" => "infinite"})
test "adds sample_threshold :infinite to query struct", %{site: site} do
q = Query.from(site, %{"period" => "30d", "sample_threshold" => "infinite"})
assert q.sample_threshold == :infinite
end
test "casts sample_threshold to integer in query struct" do
q = Query.from(@site, %{"period" => "30d", "sample_threshold" => "30000000"})
test "casts sample_threshold to integer in query struct", %{site: site} do
q = Query.from(site, %{"period" => "30d", "sample_threshold" => "30000000"})
assert q.sample_threshold == 30_000_000
end
describe "filters" do
test "parses goal filter" do
test "parses goal filter", %{site: site} do
filters = Jason.encode!(%{"goal" => "Signup"})
q = Query.from(@site, %{"period" => "6mo", "filters" => filters})
q = Query.from(site, %{"period" => "6mo", "filters" => filters})
assert q.filters["goal"] == "Signup"
end
test "parses source filter" do
test "parses source filter", %{site: site} do
filters = Jason.encode!(%{"source" => "Twitter"})
q = Query.from(@site, %{"period" => "6mo", "filters" => filters})
q = Query.from(site, %{"period" => "6mo", "filters" => filters})
assert q.filters["source"] == "Twitter"
end
test "allows prop filters when site owner is on a business plan", %{site: site, user: user} do
insert(:subscription, user: user, paddle_plan_id: @v4_business_plan_id)
filters = Jason.encode!(%{"props" => %{"author" => "!John Doe"}})
query = Query.from(site, %{"period" => "6mo", "filters" => filters})
assert Map.has_key?(query.filters, "props")
end
test "drops prop filter when site owner is on a growth plan", %{site: site, user: user} do
insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan_id)
filters = Jason.encode!(%{"props" => %{"author" => "!John Doe"}})
query = Query.from(site, %{"period" => "6mo", "filters" => filters})
refute Map.has_key?(query.filters, "props")
end
end
end

View File

@ -1,5 +1,6 @@
defmodule PlausibleWeb.Api.StatsController.CustomPropBreakdownTest do
use PlausibleWeb.ConnCase
@v4_growth_plan_id "change-me-749342"
describe "GET /api/stats/:domain/custom-prop-values/:prop_key" do
setup [:create_user, :log_in, :create_new_site, :add_imported_data]
@ -177,6 +178,17 @@ defmodule PlausibleWeb.Api.StatsController.CustomPropBreakdownTest do
}
]
end
test "errors when site owner is on a growth plan", %{conn: conn, site: site, user: user} do
insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan_id)
conn = get(conn, "/api/stats/#{site.domain}/custom-prop-values/prop?period=day")
assert json_response(conn, 402) == %{
"error" =>
"Custom Properties is part of the Plausible Business plan. To get access to this feature, please upgrade your account."
}
end
end
describe "GET /api/stats/:domain/custom-prop-values/:prop_key - with goal filter" do

View File

@ -3,6 +3,7 @@ defmodule PlausibleWeb.Api.StatsController.FunnelsTest do
@user_id 123
@other_user_id 456
@v4_growth_plan_id "change-me-749342"
@build_funnel_with [
{"page_path", "/blog/announcement"},
@ -219,6 +220,25 @@ defmodule PlausibleWeb.Api.StatsController.FunnelsTest do
]
} = resp
end
test "returns HTTP 402 when site owner is on a growth plan", %{
conn: conn,
user: user,
site: site
} do
insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan_id)
{:ok, funnel} = setup_funnel(site, @build_funnel_with)
resp =
conn
|> get("/api/stats/#{site.domain}/funnels/#{funnel.id}/?period=day")
|> json_response(402)
assert %{
"error" =>
"Funnels is part of the Plausible Business plan. To get access to this feature, please upgrade your account."
} == resp
end
end
describe "GET /api/stats/funnel - disallowed filters" do

View File

@ -1,6 +1,7 @@
defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
use PlausibleWeb.ConnCase
@v4_growth_plan_id "change-me-749342"
@user_id 123
describe "GET /api/stats/top-stats - default" do
@ -825,6 +826,28 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do
refute "Average revenue" in metrics
refute "Total revenue" in metrics
end
test "does not return average and total when site owner is on a growth plan",
%{conn: conn, site: site, user: user} do
insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan_id)
insert(:goal, site: site, event_name: "Payment", currency: "USD")
populate_stats(site, [
build(:event,
name: "Payment",
revenue_reporting_amount: Decimal.new(13_29),
revenue_reporting_currency: "USD"
)
])
filters = Jason.encode!(%{goal: "Payment"})
conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=all&filters=#{filters}")
assert %{"top_stats" => top_stats} = json_response(conn, 200)
metrics = Enum.map(top_stats, & &1["name"])
refute "Average revenue" in metrics
refute "Total revenue" in metrics
end
end
describe "GET /api/stats/top-stats - with comparisons" do

View File

@ -2,6 +2,7 @@ defmodule PlausibleWeb.StatsControllerTest do
use PlausibleWeb.ConnCase, async: false
use Plausible.Repo
import Plausible.Test.Support.HTML
@v4_growth_plan_id "change-me-749342"
describe "GET /:website - anonymous user" do
test "public site - shows site stats", %{conn: conn} do
@ -154,6 +155,20 @@ defmodule PlausibleWeb.StatsControllerTest do
assert 'utm_terms.csv' in zip
end
test "does not export custom properties when site owner is on a growth plan", %{
conn: conn,
site: site,
user: user
} do
insert(:subscription, user: user, paddle_plan_id: @v4_growth_plan_id)
response = conn |> get("/" <> site.domain <> "/export") |> response(200)
{:ok, zip} = :zip.unzip(response, [:memory])
files = Map.new(zip)
refute Map.has_key?(files, 'custom_props.csv')
end
test "exports data in zipped csvs", %{conn: conn, site: site} do
populate_exported_stats(site)
conn = get(conn, "/" <> site.domain <> "/export?date=2021-10-20")