diff --git a/assets/js/dashboard/api.js b/assets/js/dashboard/api.js
index a9ff924e44..bb1749ddfa 100644
--- a/assets/js/dashboard/api.js
+++ b/assets/js/dashboard/api.js
@@ -59,8 +59,8 @@ export function get(url, query = {}, ...extraQuery) {
return fetch(url, { signal: abortController.signal, headers: headers })
.then(response => {
if (!response.ok) {
- return response.json().then((msg) => {
- throw new ApiError(msg.error, msg)
+ return response.json().then((payload) => {
+ throw new ApiError(payload.error, payload)
})
}
return response.json()
diff --git a/assets/js/dashboard/hooks/api-client.js b/assets/js/dashboard/hooks/api-client.js
index 9e3c0fd085..903244e3a9 100644
--- a/assets/js/dashboard/hooks/api-client.js
+++ b/assets/js/dashboard/hooks/api-client.js
@@ -71,7 +71,8 @@ export function useAPIClient(props) {
const getNextPageParam = (lastPageResults, _, lastPageIndex) => {
return lastPageResults.length === LIMIT ? lastPageIndex + 1 : null
}
- const initialPageParam = 1
+ const defaultInitialPageParam = 1
+ const initialPageParam = props.initialPageParam === undefined ? defaultInitialPageParam : props.initialPageParam
return useInfiniteQuery({
queryKey: key,
diff --git a/assets/js/dashboard/stats/modals/breakdown-modal.js b/assets/js/dashboard/stats/modals/breakdown-modal.js
index a938e0e145..d97b50c3e0 100644
--- a/assets/js/dashboard/stats/modals/breakdown-modal.js
+++ b/assets/js/dashboard/stats/modals/breakdown-modal.js
@@ -6,7 +6,8 @@ import { useQueryContext } from "../../query-context";
import { useSiteContext } from "../../site-context";
import { useDebounce } from "../../custom-hooks";
import { useAPIClient } from "../../hooks/api-client";
-const MIN_HEIGHT_PX = 500
+
+export const MIN_HEIGHT_PX = 500
// The main function component for rendering the "Details" reports on the dashboard,
// i.e. a breakdown by a single (non-time) dimension, with a given set of metrics.
diff --git a/assets/js/dashboard/stats/modals/google-keywords.js b/assets/js/dashboard/stats/modals/google-keywords.js
index 4dfd459726..498489c144 100644
--- a/assets/js/dashboard/stats/modals/google-keywords.js
+++ b/assets/js/dashboard/stats/modals/google-keywords.js
@@ -1,118 +1,193 @@
-import React from "react";
-import { Link, withRouter } from 'react-router-dom'
+import React, { useState, useEffect, useRef } from "react";
import Modal from './modal'
-import * as api from '../../api'
-import numberFormatter, { percentageFormatter } from '../../util/number-formatter'
-import { parseQuery } from '../../query'
import RocketIcon from './rocket-icon'
+import { useQueryContext } from "../../query-context";
+import { useSiteContext } from "../../site-context";
+import { useAPIClient } from "../../hooks/api-client";
+import { useDebounce } from "../../custom-hooks";
+import { createVisitors, Metric, renderNumberWithTooltip } from "../reports/metrics";
+import numberFormatter, { percentageFormatter } from "../../util/number-formatter";
+import classNames from "classnames";
+import { MIN_HEIGHT_PX } from "./breakdown-modal";
-class GoogleKeywordsModal extends React.Component {
- constructor(props) {
- super(props)
- this.state = {
- loading: true,
- query: parseQuery(props.location.search, props.site)
- }
- }
+function GoogleKeywordsModal() {
+ const searchBoxRef = useRef(null)
+ const { query } = useQueryContext()
+ const site = useSiteContext()
+ const endpoint = `/api/stats/${encodeURIComponent(site.domain)}/referrers/Google`
- componentDidMount() {
- api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/Google`, this.state.query, { limit: 100 })
- .then((res) => this.setState({
- loading: false,
- searchTerms: res.search_terms,
- notConfigured: res.not_configured,
- isOwner: res.is_owner
- }))
- }
+ const [search, setSearch] = useState('')
- renderTerm(term) {
- return (
-
-
- | {term.name} |
- {numberFormatter(term.visitors)} |
- {numberFormatter(term.impressions)} |
- {percentageFormatter(term.ctr)} |
- {numberFormatter(term.position)} |
-
-
- )
- }
+ const metrics = [
+ createVisitors({renderLabel: (_query) => 'Visitors'}),
+ new Metric({key: 'impressions', renderLabel: (_query) => 'Impressions', renderValue: renderNumberWithTooltip}),
+ new Metric({key: 'ctr', renderLabel: (_query) => 'CTR', renderValue: percentageFormatter}),
+ new Metric({key: 'position', renderLabel: (_query) => 'Position', renderValue: numberFormatter})
+ ]
- renderKeywords() {
- if (this.state.notConfigured) {
- if (this.state.isOwner) {
- return (
-
-
-
The site is not connected to Google Search Keywords
-
Configure the integration to view search terms
-
Connect with Google
-
- )
- } else {
- return (
-
-
-
The site is not connected to Google Search Kewyords
-
Cannot show search terms
-
- )
+ const {
+ data,
+ hasNextPage,
+ fetchNextPage,
+ isFetchingNextPage,
+ isFetching,
+ isPending,
+ error,
+ status
+ } = useAPIClient({
+ key: [endpoint, {query, search}],
+ getRequestParams: (key) => {
+ const [_endpoint, {query, search}] = key
+ const params = { detailed: true }
+
+ return [query, search === '' ? params : {...params, search}]
+ },
+ initialPageParam: 0
+ })
+
+ useEffect(() => {
+ const searchBox = searchBoxRef.current
+
+ const handleKeyUp = (event) => {
+ if (event.key === 'Escape') {
+ event.target.blur()
+ event.stopPropagation()
}
- } else if (this.state.searchTerms.length > 0) {
- return (
-
-
-
- | Search Term |
- Visitors |
- Impressions |
- CTR |
- Position |
-
-
-
- {this.state.searchTerms.map(this.renderTerm.bind(this))}
-
-
- )
- } else {
- return (
-
-
-
Could not find any search terms for this period
-
- )
}
- }
- renderBody() {
- if (this.state.loading) {
- return (
-
- )
- } else {
- return (
-
- ← All referrers
+ searchBox.addEventListener('keyup', handleKeyUp);
-
-
- {this.renderKeywords()}
-
-
- )
+ return () => {
+ searchBox.removeEventListener('keyup', handleKeyUp);
}
- }
+ }, [])
- render() {
+ function renderRow(item) {
return (
-
- {this.renderBody()}
-
+
+ | {item.name} |
+ {metrics.map((metric) => {
+ return (
+
+ {metric.renderValue(item[metric.key])}
+ |
+ )
+ })}
+
)
}
+
+ function renderInitialLoadingSpinner() {
+ return (
+
+ )
+ }
+
+ function renderSmallLoadingSpinner() {
+ return (
+
+ )
+ }
+
+ function renderLoadMoreButton() {
+ if (isPending) return null
+ if (!isFetching && !hasNextPage) return null
+
+ return (
+
+ {!isFetching && }
+ {isFetchingNextPage && renderSmallLoadingSpinner()}
+
+ )
+ }
+
+ function handleInputChange(e) {
+ setSearch(e.target.value)
+ }
+
+ const debouncedHandleInputChange = useDebounce(handleInputChange)
+
+ function renderSearchInput() {
+ const searchBoxClass = classNames('shadow-sm dark:bg-gray-900 dark:text-gray-100 focus:ring-indigo-500 focus:border-indigo-500 block sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:bg-gray-800 w-48', {
+ 'pointer-events-none' : status === 'error'
+ })
+ return (
+
+ )
+ }
+
+ function renderModalBody() {
+ if (data?.pages?.length) {
+ return (
+
+
+
+
+ |
+ Search term
+ |
+ {metrics.map((metric) => {
+ return (
+
+ {metric.renderLabel(query)}
+ |
+ )
+ })}
+
+
+
+ {data.pages.map((p) => p.map(renderRow))}
+
+
+
+ )
+ }
+ }
+
+ function renderError() {
+ return (
+
+ )
+ }
+
+ return (
+
+
+
+
+
Google Search Terms
+ {!isPending && isFetching && renderSmallLoadingSpinner()}
+
+ {renderSearchInput()}
+
+
+
+ {status === 'error' && renderError()}
+ {isPending && renderInitialLoadingSpinner()}
+ {!isPending && renderModalBody()}
+ {renderLoadMoreButton()}
+
+
+
+ )
}
-export default withRouter(GoogleKeywordsModal)
+export default GoogleKeywordsModal
diff --git a/assets/js/dashboard/stats/reports/metrics.js b/assets/js/dashboard/stats/reports/metrics.js
index db5704e66f..be5a75e91e 100644
--- a/assets/js/dashboard/stats/reports/metrics.js
+++ b/assets/js/dashboard/stats/reports/metrics.js
@@ -159,6 +159,6 @@ export const createExitRate = (props) => {
return new Metric({...props, key: "exit_rate", renderValue, renderLabel})
}
-function renderNumberWithTooltip(value) {
+export function renderNumberWithTooltip(value) {
return {numberFormatter(value)}
}
\ No newline at end of file
diff --git a/assets/js/dashboard/stats/sources/search-terms.js b/assets/js/dashboard/stats/sources/search-terms.js
index 707c5177f2..2b8660d9fe 100644
--- a/assets/js/dashboard/stats/sources/search-terms.js
+++ b/assets/js/dashboard/stats/sources/search-terms.js
@@ -7,10 +7,19 @@ import RocketIcon from '../modals/rocket-icon'
import * as api from '../../api'
import LazyLoader from '../../components/lazy-loader'
+export function ConfigureSearchTermsCTA({site}) {
+ return (
+ <>
+ Configure the integration to view search terms
+ Connect with Google
+ >
+ )
+}
+
export default class SearchTerms extends React.Component {
constructor(props) {
super(props)
- this.state = { loading: true }
+ this.state = { loading: true, errorPayload: null }
this.onVisible = this.onVisible.bind(this)
this.fetchSearchTerms = this.fetchSearchTerms.bind(this)
}
@@ -37,14 +46,11 @@ export default class SearchTerms extends React.Component {
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/referrers/Google`, this.props.query)
.then((res) => this.setState({
loading: false,
- searchTerms: res.search_terms || [],
- notConfigured: res.not_configured,
- isAdmin: res.is_admin,
- unsupportedFilters: res.unsupported_filters
+ searchTerms: res.results,
+ errorPayload: null
})).catch((error) => {
- this.setState({ loading: false, searchTerms: [], notConfigured: true, error: true, isAdmin: error.payload.is_admin })
- }
- )
+ this.setState({ loading: false, searchTerms: [], errorPayload: error.payload })
+ })
}
renderSearchTerm(term) {
@@ -68,22 +74,14 @@ export default class SearchTerms extends React.Component {
}
renderList() {
- if (this.state.unsupportedFilters) {
+ if (this.state.errorPayload) {
+ const {reason, is_admin, error} = this.state.errorPayload
+
return (
-
Unable to fetch keyword data from Search Console because it does not support the current set of filters
-
- )
- } else if (this.state.notConfigured) {
- return (
-
-
-
- This site is not connected to Search Console so we cannot show the search terms
- {this.state.isAdmin && this.state.error && <>
Please click below to connect your Search Console account.
>}
-
- {this.state.isAdmin &&
Connect with Google}
+
{error}
+ {reason === 'not_configured' && is_admin &&
}
)
} else if (this.state.searchTerms.length > 0) {
diff --git a/fixture/http_mocks/google_analytics_stats#with_page.json b/fixture/http_mocks/google_analytics_stats#with_page.json
deleted file mode 100644
index f2fb9a6d95..0000000000
--- a/fixture/http_mocks/google_analytics_stats#with_page.json
+++ /dev/null
@@ -1,50 +0,0 @@
-[
- {
- "status": 200,
- "url": "https://www.googleapis.com/webmasters/v3/sites/sc-domain%3Adummy.test/searchAnalytics/query",
- "method": "post",
- "request_body": {
- "dimensionFilterGroups": [
- {
- "filters": [
- {
- "dimension": "page",
- "expression": "https://sc-domain%3Adummy.test5"
- }
- ]
- }
- ],
- "dimensions": [
- "query"
- ],
- "endDate": "2022-01-05",
- "rowLimit": 5,
- "startDate": "2022-01-01"
- },
- "response_body": {
- "responseAggregationType": "auto",
- "rows": [
- {
- "clicks": 25.0,
- "ctr": 0.3,
- "impressions": 50.0,
- "keys": [
- "keyword1",
- "keyword2"
- ],
- "position": 2.0
- },
- {
- "clicks": 15.0,
- "ctr": 0.5,
- "impressions": 25.0,
- "keys": [
- "keyword3",
- "keyword4"
- ],
- "position": 4.0
- }
- ]
- }
- }
-]
\ No newline at end of file
diff --git a/fixture/http_mocks/google_analytics_auth#invalid_grant.json b/fixture/http_mocks/google_auth#invalid_grant.json
similarity index 100%
rename from fixture/http_mocks/google_analytics_auth#invalid_grant.json
rename to fixture/http_mocks/google_auth#invalid_grant.json
diff --git a/fixture/http_mocks/google_analytics_stats.json b/fixture/http_mocks/google_search_console.json
similarity index 97%
rename from fixture/http_mocks/google_analytics_stats.json
rename to fixture/http_mocks/google_search_console.json
index 431b8fa2a7..994873088f 100644
--- a/fixture/http_mocks/google_analytics_stats.json
+++ b/fixture/http_mocks/google_search_console.json
@@ -10,6 +10,7 @@
],
"endDate": "2022-01-05",
"rowLimit": 5,
+ "startRow": 0,
"startDate": "2022-01-01"
},
"response_body": {
diff --git a/lib/plausible/google/api.ex b/lib/plausible/google/api.ex
index c36dc1df92..7f186e4779 100644
--- a/lib/plausible/google/api.ex
+++ b/lib/plausible/google/api.ex
@@ -59,18 +59,18 @@ defmodule Plausible.Google.API do
end
end
- def fetch_stats(site, query, limit) do
+ def fetch_stats(site, query, pagination, search) do
with {:ok, site} <- ensure_search_console_property(site),
{:ok, access_token} <- maybe_refresh_token(site.google_auth),
- {:ok, search_console_filters} <-
- SearchConsole.Filters.transform(site.google_auth.property, query.filters),
+ {:ok, gsc_filters} <-
+ SearchConsole.Filters.transform(site.google_auth.property, query.filters, search),
{:ok, stats} <-
HTTP.list_stats(
access_token,
site.google_auth.property,
query.date_range,
- limit,
- search_console_filters
+ pagination,
+ gsc_filters
) do
stats
|> Map.get("rows", [])
diff --git a/lib/plausible/google/http.ex b/lib/plausible/google/http.ex
index c24808764b..4666cd0a44 100644
--- a/lib/plausible/google/http.ex
+++ b/lib/plausible/google/http.ex
@@ -39,12 +39,15 @@ defmodule Plausible.Google.HTTP do
response.body
end
- def list_stats(access_token, property, date_range, limit, search_console_filters) do
+ def list_stats(access_token, property, date_range, pagination, search_console_filters) do
+ {limit, page} = pagination
+
params = %{
startDate: Date.to_iso8601(date_range.first),
endDate: Date.to_iso8601(date_range.last),
dimensions: ["query"],
rowLimit: limit,
+ startRow: page * limit,
dimensionFilterGroups: search_console_filters
}
@@ -65,7 +68,7 @@ defmodule Plausible.Google.HTTP do
{:error, error}
{:error, reason} ->
- Logger.error("Google Analytics: failed to list stats: #{inspect(reason)}")
+ Logger.error("Google Search Console: failed to list stats: #{inspect(reason)}")
{:error, "failed_to_list_stats"}
end
end
diff --git a/lib/plausible/google/search_console/filters.ex b/lib/plausible/google/search_console/filters.ex
index 43e10e1c87..beca3923dc 100644
--- a/lib/plausible/google/search_console/filters.ex
+++ b/lib/plausible/google/search_console/filters.ex
@@ -2,17 +2,18 @@ defmodule Plausible.Google.SearchConsole.Filters do
@moduledoc false
import Plausible.Stats.Filters.Utils, only: [page_regex: 1]
- def transform(property, plausible_filters) do
- search_console_filters =
- Enum.reduce_while(plausible_filters, [], fn plausible_filter, search_console_filters ->
+ def transform(property, plausible_filters, search) do
+ gsc_filters =
+ Enum.reduce_while(plausible_filters, [], fn plausible_filter, gsc_filters ->
case transform_filter(property, plausible_filter) do
:unsupported -> {:halt, :unsupported_filters}
- :ignore -> {:cont, search_console_filters}
- search_console_filter -> {:cont, [search_console_filter | search_console_filters]}
+ :ignore -> {:cont, gsc_filters}
+ gsc_filter -> {:cont, [gsc_filter | gsc_filters]}
end
end)
+ |> maybe_add_search_filter(search)
- case search_console_filters do
+ case gsc_filters do
:unsupported_filters -> :unsupported_filters
[] -> {:ok, []}
filters when is_list(filters) -> {:ok, [%{filters: filters}]}
@@ -64,4 +65,10 @@ defmodule Plausible.Google.SearchConsole.Filters do
country = Location.Country.get_country(alpha_2)
country.alpha_3
end
+
+ defp maybe_add_search_filter(gsc_filters, search) when byte_size(search) > 0 do
+ [%{operator: "includingRegex", expression: search, dimension: "query"} | gsc_filters]
+ end
+
+ defp maybe_add_search_filter(gsc_filters, _search), do: gsc_filters
end
diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex
index bfc34cbd87..6a65a7903f 100644
--- a/lib/plausible_web/controllers/api/stats_controller.ex
+++ b/lib/plausible_web/controllers/api/stats_controller.ex
@@ -752,23 +752,46 @@ defmodule PlausibleWeb.Api.StatsController do
user_id = get_session(conn, :current_user_id)
is_admin = user_id && Plausible.Sites.has_admin_access?(user_id, site)
- case google_api().fetch_stats(site, query, params["limit"] || 9) do
- {:error, :google_propery_not_configured} ->
- json(conn, %{not_configured: true, is_admin: is_admin})
+ pagination = {
+ to_int(params["limit"], 9),
+ to_int(params["page"], 0)
+ }
+
+ search = params["search"] || ""
+
+ not_configured_error_payload =
+ %{
+ error: "The site is not connected to Google Search Keywords",
+ reason: :not_configured,
+ is_admin: is_admin
+ }
+
+ unsupported_filters_error_payload = %{
+ error:
+ "Unable to fetch keyword data from Search Console because it does not support the current set of filters",
+ reason: :unsupported_filters
+ }
+
+ case google_api().fetch_stats(site, query, pagination, search) do
+ {:error, :google_property_not_configured} ->
+ conn
+ |> put_status(422)
+ |> json(not_configured_error_payload)
{:error, :unsupported_filters} ->
- json(conn, %{unsupported_filters: true})
+ conn
+ |> put_status(422)
+ |> json(unsupported_filters_error_payload)
{:ok, terms} ->
- json(conn, %{search_terms: terms})
+ json(conn, %{results: terms})
+
+ {:error, error} ->
+ Logger.error("Plausible.Google.API.fetch_stats failed with error: `#{inspect(error)}`")
- {:error, _} ->
conn
|> put_status(502)
- |> json(%{
- not_configured: true,
- is_admin: is_admin
- })
+ |> json(not_configured_error_payload)
end
end
diff --git a/test/plausible/google/api_test.exs b/test/plausible/google/api_test.exs
index ce22268a98..cd020ca5ec 100644
--- a/test/plausible/google/api_test.exs
+++ b/test/plausible/google/api_test.exs
@@ -34,6 +34,7 @@ defmodule Plausible.Google.APITest do
dimensions: ["query"],
endDate: "2022-01-05",
rowLimit: 5,
+ startRow: 0,
startDate: "2022-01-01"
} ->
{:error, %{reason: %Finch.Response{status: Enum.random([401, 403])}}}
@@ -42,7 +43,7 @@ defmodule Plausible.Google.APITest do
query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])}
- assert {:error, "google_auth_error"} = Google.API.fetch_stats(site, query, 5)
+ assert {:error, "google_auth_error"} = Google.API.fetch_stats(site, query, {5, 0}, "")
end
test "returns whatever error code google returns on API client error", %{site: site} do
@@ -59,7 +60,7 @@ defmodule Plausible.Google.APITest do
query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])}
- assert {:error, "some_error"} = Google.API.fetch_stats(site, query, 5)
+ assert {:error, "some_error"} = Google.API.fetch_stats(site, query, {5, 0}, "")
end
test "returns generic HTTP error and logs it", %{site: site} do
@@ -79,15 +80,16 @@ defmodule Plausible.Google.APITest do
log =
capture_log(fn ->
assert {:error, "failed_to_list_stats"} =
- Google.API.fetch_stats(site, query, 5)
+ Google.API.fetch_stats(site, query, {5, 0}, "")
end)
- assert log =~ "Google Analytics: failed to list stats: %Finch.Error{reason: :some_reason}"
+ assert log =~
+ "Google Search Console: failed to list stats: %Finch.Error{reason: :some_reason}"
end
end
test "returns error when token refresh fails", %{user: user, site: site} do
- mock_http_with("google_analytics_auth#invalid_grant.json")
+ mock_http_with("google_auth#invalid_grant.json")
insert(:google_auth,
user: user,
@@ -100,13 +102,13 @@ defmodule Plausible.Google.APITest do
query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])}
- assert {:error, "invalid_grant"} = Google.API.fetch_stats(site, query, 5)
+ assert {:error, "invalid_grant"} = Google.API.fetch_stats(site, query, 5, "")
end
test "returns error when google auth not configured", %{site: site} do
query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])}
- assert {:error, :google_property_not_configured} = Google.API.fetch_stats(site, query, 5)
+ assert {:error, :google_property_not_configured} = Google.API.fetch_stats(site, query, 5, "")
end
describe "fetch_stats/3 with valid auth" do
@@ -122,7 +124,7 @@ defmodule Plausible.Google.APITest do
end
test "returns name and visitor count", %{site: site} do
- mock_http_with("google_analytics_stats.json")
+ mock_http_with("google_search_console.json")
query = %Plausible.Stats.Query{date_range: Date.range(~D[2022-01-01], ~D[2022-01-05])}
@@ -130,7 +132,7 @@ defmodule Plausible.Google.APITest do
[
%{name: "keyword1", visitors: 25, ctr: 36.8, impressions: 50, position: 2.2},
%{name: "keyword3", visitors: 15}
- ]} = Google.API.fetch_stats(site, query, 5)
+ ]} = Google.API.fetch_stats(site, query, {5, 0}, "")
end
test "transforms page filters to search console format", %{site: site} do
@@ -147,6 +149,7 @@ defmodule Plausible.Google.APITest do
dimensions: ["query"],
endDate: "2022-01-05",
rowLimit: 5,
+ startRow: 0,
startDate: "2022-01-01"
} ->
{:ok, %Finch.Response{status: 200, body: %{"rows" => []}}}
@@ -161,7 +164,7 @@ defmodule Plausible.Google.APITest do
"filters" => "event:page==/page"
})
- assert {:ok, []} = Google.API.fetch_stats(site, query, 5)
+ assert {:ok, []} = Google.API.fetch_stats(site, query, {5, 0}, "")
end
test "returns :invalid filters when using filters that cannot be used in Search Console", %{
@@ -175,7 +178,7 @@ defmodule Plausible.Google.APITest do
"filters" => "event:goal==Signup"
})
- assert {:error, :unsupported_filters} = Google.API.fetch_stats(site, query, 5)
+ assert {:error, :unsupported_filters} = Google.API.fetch_stats(site, query, 5, "")
end
end
end
diff --git a/test/plausible/google/search_console/filters_test.exs b/test/plausible/google/search_console/filters_test.exs
index 265df74762..83b0102cc6 100644
--- a/test/plausible/google/search_console/filters_test.exs
+++ b/test/plausible/google/search_console/filters_test.exs
@@ -7,7 +7,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:entry_page", ["/page"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -27,7 +27,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:matches, "visit:entry_page", ["*page*"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -47,7 +47,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:entry_page", ["/pageA", "/pageB"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -67,7 +67,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:matches, "visit:entry_page", ["/pageA*", "/pageB*"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -87,7 +87,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:matches, "event:page", ["/pageA*", "/pageB*"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -107,7 +107,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:screen", ["Desktop"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -123,7 +123,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:screen", ["Mobile", "Tablet"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -139,7 +139,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:country", ["EE"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{filters: [%{dimension: "country", operator: "includingRegex", expression: "EST"}]}
@@ -151,7 +151,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:country", ["EE", "PL"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -169,7 +169,7 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:screen", ["Desktop"]]
]
- {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters)
+ {:ok, transformed} = Filters.transform("sc-domain:plausible.io", filters, "")
assert transformed == [
%{
@@ -194,6 +194,6 @@ defmodule Plausible.Google.SearchConsole.FiltersTest do
[:is, "visit:utm_medium", "facebook"]
]
- assert :unsupported_filters = Filters.transform("sc-domain:plausible.io", filters)
+ assert :unsupported_filters = Filters.transform("sc-domain:plausible.io", filters, "")
end
end
diff --git a/test/plausible_web/controllers/api/stats_controller/sources_test.exs b/test/plausible_web/controllers/api/stats_controller/sources_test.exs
index ea7b9c15a2..ba3184959e 100644
--- a/test/plausible_web/controllers/api/stats_controller/sources_test.exs
+++ b/test/plausible_web/controllers/api/stats_controller/sources_test.exs
@@ -1626,9 +1626,9 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do
])
conn = get(conn, "/api/stats/#{site.domain}/referrers/Google?period=day")
- {:ok, terms} = Plausible.Google.API.Mock.fetch_stats(nil, nil, nil)
+ {:ok, terms} = Plausible.Google.API.Mock.fetch_stats(nil, nil, nil, nil)
- assert json_response(conn, 200) == %{"search_terms" => terms}
+ assert json_response(conn, 200) == %{"results" => terms}
end
test "works when filter expression is provided for source", %{
diff --git a/test/support/google_api_mock.ex b/test/support/google_api_mock.ex
index 878cbec970..84329a6547 100644
--- a/test/support/google_api_mock.ex
+++ b/test/support/google_api_mock.ex
@@ -3,7 +3,7 @@ defmodule Plausible.Google.API.Mock do
Mock of API to Google services.
"""
- def fetch_stats(_auth, _query, _limit) do
+ def fetch_stats(_auth, _query, _pagination, _search) do
{:ok,
[
%{"name" => "simple web analytics", "count" => 6},