Skip to content

Commit dbd4131

Browse files
apataRobertJoonas
andauthored
Silently ignore missing goals (#6162)
* Add failing tests * Fix issue with top stats not loading for shared links limited to segment * Add tests for ignoring missing goals * Silently ignore missing goals * Silently ignore missing goals for internal query API --------- Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
1 parent c67c316 commit dbd4131

4 files changed

Lines changed: 78 additions & 4 deletions

File tree

lib/plausible/stats/dashboard/query_parser.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ defmodule Plausible.Stats.Dashboard.QueryParser do
2626
relative_date: relative_date,
2727
filters: filters,
2828
metrics: metrics,
29-
include: include
29+
include: include,
30+
skip_goal_existence_check: true
3031
})}
3132
end
3233
end

lib/plausible/stats/parsed_query_params.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ defmodule Plausible.Stats.ParsedQueryParams do
1414
order_by: nil,
1515
pagination: nil,
1616
now: nil,
17-
include: %Plausible.Stats.QueryInclude{}
17+
include: %Plausible.Stats.QueryInclude{},
18+
# When true, skips the validation that goal names in `is` filters must be
19+
# configured for the site. Missing goals simply match nothing in SQL, matching
20+
# the behaviour of legacy endpoints like top_stats.
21+
skip_goal_existence_check: false
1822

1923
def new!(params) when is_map(params) do
2024
struct!(__MODULE__, Map.to_list(params))

lib/plausible/stats/query_builder.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ defmodule Plausible.Stats.QueryBuilder do
3535
:ok <- validate_toplevel_only_filter_dimension(query),
3636
:ok <- validate_special_metrics_filters(query),
3737
:ok <- validate_behavioral_filters(query),
38-
:ok <- validate_filtered_goals_exist(query),
38+
:ok <- validate_filtered_goals_exist(query, parsed_query_params),
3939
:ok <- validate_revenue_metrics_access(site, query),
4040
:ok <- validate_metrics(query),
4141
:ok <- validate_include(query) do
@@ -382,7 +382,10 @@ defmodule Plausible.Stats.QueryBuilder do
382382
end)
383383
end
384384

385-
defp validate_filtered_goals_exist(query) do
385+
defp validate_filtered_goals_exist(_query, %ParsedQueryParams{skip_goal_existence_check: true}),
386+
do: :ok
387+
388+
defp validate_filtered_goals_exist(query, %ParsedQueryParams{}) do
386389
# Note: We don't check :contains goal filters since it's acceptable if they match nothing.
387390
goal_filter_clauses =
388391
query.filters

test/plausible_web/controllers/api/stats_controller/query_controller_test.exs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,71 @@ defmodule PlausibleWeb.Api.InternalController.QueryTest do
133133

134134
assert json_response(conn, 200)["results"] == [%{"metrics" => [3], "dimensions" => []}]
135135
end
136+
137+
test "silently ignores missing event goal in filter", %{conn: conn, site: site} do
138+
populate_stats(site, [
139+
build(:event, name: "Signup", timestamp: ~N[2021-01-01 00:00:00])
140+
])
141+
142+
conn =
143+
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
144+
"metrics" => ["visitors"],
145+
"date_range" => "all",
146+
"filters" => [["is", "event:goal", ["Missing goal"]]]
147+
})
148+
149+
assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
150+
end
151+
152+
test "silently ignores missing pageview goal in filter", %{conn: conn, site: site} do
153+
populate_stats(site, [
154+
build(:pageview, pathname: "/register", timestamp: ~N[2021-01-01 00:00:00])
155+
])
156+
157+
conn =
158+
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
159+
"metrics" => ["visitors"],
160+
"date_range" => "all",
161+
"filters" => [["is", "event:goal", ["Visit /"]]]
162+
})
163+
164+
assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
165+
end
166+
167+
test "silently ignores missing goal and still returns results for existing goal in filter",
168+
%{conn: conn, site: site} do
169+
insert(:goal, %{site: site, event_name: "Signup"})
170+
171+
populate_stats(site, [
172+
build(:event, name: "Signup", user_id: 1, timestamp: ~N[2021-01-01 00:00:00]),
173+
build(:event, name: "Purchase", user_id: 2, timestamp: ~N[2021-01-01 00:00:00])
174+
])
175+
176+
conn =
177+
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
178+
"metrics" => ["visitors"],
179+
"date_range" => "all",
180+
"filters" => [["is", "event:goal", ["Signup", "Missing goal"]]]
181+
})
182+
183+
assert json_response(conn, 200)["results"] == [%{"metrics" => [1], "dimensions" => []}]
184+
end
185+
186+
test "silently ignores missing goal in has_done behavioral filter",
187+
%{conn: conn, site: site} do
188+
populate_stats(site, [
189+
build(:pageview, timestamp: ~N[2021-01-01 00:00:00]),
190+
build(:pageview, timestamp: ~N[2021-01-01 00:00:00])
191+
])
192+
193+
conn =
194+
post(conn, "/api/stats/#{URI.encode(site.domain)}/query", %{
195+
"metrics" => ["visitors"],
196+
"date_range" => "all",
197+
"filters" => [["has_done", ["is", "event:goal", ["Missing goal"]]]]
198+
})
199+
200+
assert json_response(conn, 200)["results"] == [%{"metrics" => [0], "dimensions" => []}]
201+
end
136202
end
137203
end

0 commit comments

Comments
 (0)