mirror of
https://github.com/plausible/analytics.git
synced 2024-09-11 18:07:33 +03:00
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:
parent
9b912f3d89
commit
896d78d8fd
@ -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 (
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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] %>"
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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} ==
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user