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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/plausible/stats/dashboard/query_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ defmodule Plausible.Stats.Dashboard.QueryParser do
relative_date: relative_date,
filters: filters,
metrics: metrics,
include: include
include: include,
skip_goal_existence_check: true
})}
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/plausible/stats/parsed_query_params.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ defmodule Plausible.Stats.ParsedQueryParams do
order_by: nil,
pagination: nil,
now: nil,
include: %Plausible.Stats.QueryInclude{}
include: %Plausible.Stats.QueryInclude{},
# When true, skips the validation that goal names in `is` filters must be
# configured for the site. Missing goals simply match nothing in SQL, matching
# the behaviour of legacy endpoints like top_stats.
skip_goal_existence_check: false

def new!(params) when is_map(params) do
struct!(__MODULE__, Map.to_list(params))
Expand Down
7 changes: 5 additions & 2 deletions lib/plausible/stats/query_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ defmodule Plausible.Stats.QueryBuilder do
:ok <- validate_toplevel_only_filter_dimension(query),
:ok <- validate_special_metrics_filters(query),
:ok <- validate_behavioral_filters(query),
:ok <- validate_filtered_goals_exist(query),
:ok <- validate_filtered_goals_exist(query, parsed_query_params),
:ok <- validate_revenue_metrics_access(site, query),
:ok <- validate_metrics(query),
:ok <- validate_include(query) do
Expand Down Expand Up @@ -382,7 +382,10 @@ defmodule Plausible.Stats.QueryBuilder do
end)
end

defp validate_filtered_goals_exist(query) do
defp validate_filtered_goals_exist(_query, %ParsedQueryParams{skip_goal_existence_check: true}),
do: :ok

defp validate_filtered_goals_exist(query, %ParsedQueryParams{}) do
# Note: We don't check :contains goal filters since it's acceptable if they match nothing.
goal_filter_clauses =
query.filters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,71 @@ defmodule PlausibleWeb.Api.InternalController.QueryTest do

assert json_response(conn, 200)["results"] == [%{"metrics" => [3], "dimensions" => []}]
end

test "silently ignores missing event goal in filter", %{conn: conn, site: site} do
populate_stats(site, [
build(:event, name: "Signup", timestamp: ~N[2021-01-01 00:00:00])
])

conn =
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
"metrics" => ["visitors"],
"date_range" => "all",
"filters" => [["is", "event:goal", ["Missing goal"]]]
})

assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
end

test "silently ignores missing pageview goal in filter", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, pathname: "/register", timestamp: ~N[2021-01-01 00:00:00])
])

conn =
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
"metrics" => ["visitors"],
"date_range" => "all",
"filters" => [["is", "event:goal", ["Visit /"]]]
})

assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
end

test "silently ignores missing goal and still returns results for existing goal in filter",
%{conn: conn, site: site} do
insert(:goal, %{site: site, event_name: "Signup"})

populate_stats(site, [
build(:event, name: "Signup", user_id: 1, timestamp: ~N[2021-01-01 00:00:00]),
build(:event, name: "Purchase", user_id: 2, timestamp: ~N[2021-01-01 00:00:00])
])

conn =
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
"metrics" => ["visitors"],
"date_range" => "all",
"filters" => [["is", "event:goal", ["Signup", "Missing goal"]]]
})

assert json_response(conn, 200)["results"] == [%{"metrics" => [1], "dimensions" => []}]
end

test "silently ignores missing goal in has_done behavioral filter",
%{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, timestamp: ~N[2021-01-01 00:00:00]),
build(:pageview, timestamp: ~N[2021-01-01 00:00:00])
])

conn =
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
"metrics" => ["visitors"],
"date_range" => "all",
"filters" => [["has_done", ["is", "event:goal", ["Missing goal"]]]]
})

assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
end
end
end
Loading