mirror of
https://github.com/plausible/analytics.git
synced 2024-09-11 18:07:33 +03:00
Unify UA and GA4 import flow into one (#3888)
* Unify GA4 and UA import flow into one * Clean up property and view data retrieval via Google HTTP APIs * Turn `Map.get` into `Map.fetch!` in API response processing code * Bump list account summaries page size limit to max of 200 * Show only views in legacy flow and fix legacy redirect after import start * Move google analytics import actions tests to a separate module * Extend Google Analytics controller tests * DRY up `property?` predicate (h/t @RobertJoonas)
This commit is contained in:
parent
5f9465614b
commit
d6e81670e4
12
fixture/ga4_get_property.json
Normal file
12
fixture/ga4_get_property.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"account": "accounts/28425555",
|
||||
"createTime": "2024-02-22T10:50:15.462Z",
|
||||
"currencyCode": "USD",
|
||||
"displayName": "account.one - GA4",
|
||||
"name": "properties/428685444",
|
||||
"parent": "accounts/28425555",
|
||||
"propertyType": "PROPERTY_TYPE_ORDINARY",
|
||||
"serviceLevel": "GOOGLE_ANALYTICS_STANDARD",
|
||||
"timeZone": "Europe/Warsaw",
|
||||
"updateTime": "2024-02-22T10:50:15.462Z"
|
||||
}
|
38
fixture/ga_start_date.json
Normal file
38
fixture/ga_start_date.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"reports": [
|
||||
{
|
||||
"columnHeader": {
|
||||
"dimensions": [
|
||||
"ga:date"
|
||||
],
|
||||
"metricHeader": {
|
||||
"metricHeaderEntries": [
|
||||
{
|
||||
"name": "ga:pageviews",
|
||||
"type": "INTEGER"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"isDataGolden": true,
|
||||
"rowCount": 849,
|
||||
"rows": [
|
||||
{
|
||||
"dimensions": [
|
||||
"20120118"
|
||||
],
|
||||
"metrics": [
|
||||
{
|
||||
"values": [
|
||||
"37"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"nextPageToken": "1"
|
||||
}
|
||||
]
|
||||
}
|
@ -23,16 +23,38 @@ defmodule Plausible.Google.API do
|
||||
|
||||
def import_authorize_url(site_id, redirect_to, opts \\ []) do
|
||||
legacy = Keyword.get(opts, :legacy, true)
|
||||
ga4 = Keyword.get(opts, :ga4, false)
|
||||
|
||||
"https://accounts.google.com/o/oauth2/v2/auth?client_id=#{client_id()}&redirect_uri=#{redirect_uri()}&prompt=consent&response_type=code&access_type=offline&scope=#{@import_scope}&state=" <>
|
||||
Jason.encode!([site_id, redirect_to, legacy, ga4])
|
||||
Jason.encode!([site_id, redirect_to, legacy])
|
||||
end
|
||||
|
||||
def fetch_access_token!(code) do
|
||||
HTTP.fetch_access_token!(code)
|
||||
end
|
||||
|
||||
def list_properties_and_views(access_token) do
|
||||
with {:ok, properties} <- Plausible.Google.GA4.API.list_properties(access_token),
|
||||
{:ok, views} <- Plausible.Google.UA.API.list_views(access_token) do
|
||||
{:ok, properties ++ views}
|
||||
end
|
||||
end
|
||||
|
||||
def get_property_or_view(access_token, property_or_view) do
|
||||
if property?(property_or_view) do
|
||||
Plausible.Google.GA4.API.get_property(access_token, property_or_view)
|
||||
else
|
||||
Plausible.Google.UA.API.get_view(access_token, property_or_view)
|
||||
end
|
||||
end
|
||||
|
||||
def get_analytics_start_date(access_token, property_or_view) do
|
||||
if property?(property_or_view) do
|
||||
Plausible.Google.GA4.API.get_analytics_start_date(access_token, property_or_view)
|
||||
else
|
||||
Plausible.Google.UA.API.get_analytics_start_date(access_token, property_or_view)
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_verified_properties(auth) do
|
||||
with {:ok, access_token} <- maybe_refresh_token(auth),
|
||||
{:ok, sites} <- Plausible.Google.HTTP.list_sites(access_token) do
|
||||
@ -90,6 +112,8 @@ defmodule Plausible.Google.API do
|
||||
end
|
||||
end
|
||||
|
||||
def property?(value), do: String.starts_with?(value, "properties/")
|
||||
|
||||
defp do_refresh_token(refresh_token) do
|
||||
case HTTP.refresh_auth_token(refresh_token) do
|
||||
{:ok, %{"access_token" => new_access_token, "expires_in" => expires_in}} ->
|
||||
|
@ -25,29 +25,35 @@ defmodule Plausible.Google.GA4.API do
|
||||
accounts
|
||||
|> Enum.filter(& &1["propertySummaries"])
|
||||
|> Enum.map(fn account ->
|
||||
{"#{account["displayName"]} (#{account["account"]})",
|
||||
%{"account" => account_id, "displayName" => account_name} = account
|
||||
|
||||
{"#{account_name} (#{account_id})",
|
||||
Enum.map(account["propertySummaries"], fn property ->
|
||||
{"#{property["displayName"]} (#{property["property"]})", property["property"]}
|
||||
%{"displayName" => property_name, "property" => property_id} = property
|
||||
|
||||
{"#{property_name} (#{property_id})", property_id}
|
||||
end)}
|
||||
end)
|
||||
|
||||
{:ok, accounts}
|
||||
|
||||
error ->
|
||||
error
|
||||
{:error, cause} ->
|
||||
{:error, cause}
|
||||
end
|
||||
end
|
||||
|
||||
def get_property(access_token, lookup_property) do
|
||||
case list_properties(access_token) do
|
||||
{:ok, properties} ->
|
||||
property =
|
||||
properties
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> List.flatten()
|
||||
|> Enum.find(fn {_name, property} -> property == lookup_property end)
|
||||
case GA4.HTTP.get_property(access_token, lookup_property) do
|
||||
{:ok, property} ->
|
||||
%{"displayName" => property_name, "name" => property_id, "account" => account_id} =
|
||||
property
|
||||
|
||||
{:ok, property}
|
||||
{:ok,
|
||||
%{
|
||||
id: property_id,
|
||||
name: "#{property_name} (#{property_id})",
|
||||
account_id: account_id
|
||||
}}
|
||||
|
||||
{:error, cause} ->
|
||||
{:error, cause}
|
||||
|
@ -49,9 +49,8 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||
)
|
||||
|
||||
with {:ok, %{body: body}} <- response,
|
||||
# File.write!("fixture/ga4_report_#{report_request.dataset}.json", Jason.encode!(body)),
|
||||
{:ok, report} <- parse_report_from_response(body),
|
||||
row_count <- Map.get(report, "rowCount"),
|
||||
row_count <- Map.fetch!(report, "rowCount"),
|
||||
{:ok, report} <- convert_to_maps(report) do
|
||||
{:ok, {report, row_count}}
|
||||
else
|
||||
@ -130,7 +129,7 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||
end
|
||||
|
||||
def list_accounts_for_user(access_token) do
|
||||
url = "#{admin_api_url()}/v1beta/accountSummaries"
|
||||
url = "#{admin_api_url()}/v1beta/accountSummaries?pageSize=200"
|
||||
|
||||
headers = [{"Authorization", "Bearer #{access_token}"}]
|
||||
|
||||
@ -147,6 +146,30 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||
end
|
||||
end
|
||||
|
||||
def get_property(access_token, property) do
|
||||
url = "#{admin_api_url()}/v1beta/#{property}"
|
||||
|
||||
headers = [{"Authorization", "Bearer #{access_token}"}]
|
||||
|
||||
case HTTPClient.impl().get(url, headers) do
|
||||
{:ok, %Finch.Response{body: body, status: 200}} ->
|
||||
{:ok, body}
|
||||
|
||||
{:error, %HTTPClient.Non200Error{} = error} when error.reason.status in [401, 403] ->
|
||||
{:error, :authentication_failed}
|
||||
|
||||
{:error, %HTTPClient.Non200Error{} = error} when error.reason.status in [404] ->
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, %HTTPClient.Non200Error{} = error} ->
|
||||
Sentry.capture_message("Error retrieving Google property #{property}",
|
||||
extra: %{error: error}
|
||||
)
|
||||
|
||||
{:error, :unknown}
|
||||
end
|
||||
end
|
||||
|
||||
@earliest_valid_date "2015-08-14"
|
||||
def get_analytics_start_date(access_token, property) do
|
||||
params = %{
|
||||
|
@ -26,7 +26,11 @@ defmodule Plausible.Google.UA.API do
|
||||
def list_views(access_token) do
|
||||
case UA.HTTP.list_views_for_user(access_token) do
|
||||
{:ok, %{"items" => views}} ->
|
||||
views = Enum.group_by(views, &view_hostname/1, &view_names/1)
|
||||
views =
|
||||
views
|
||||
|> Enum.group_by(&view_hostname/1, &view_names/1)
|
||||
|> Enum.sort_by(fn {key, _} -> key end)
|
||||
|
||||
{:ok, views}
|
||||
|
||||
error ->
|
||||
@ -40,18 +44,19 @@ defmodule Plausible.Google.UA.API do
|
||||
Returns a single Google Analytics view if the user has access to it.
|
||||
"""
|
||||
def get_view(access_token, lookup_id) do
|
||||
case list_views(access_token) do
|
||||
{:ok, views} ->
|
||||
view =
|
||||
views
|
||||
|> Map.values()
|
||||
|> List.flatten()
|
||||
|> Enum.find(fn {_name, id} -> id == lookup_id end)
|
||||
with {:ok, views} <- list_views(access_token) do
|
||||
views =
|
||||
views
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|> List.flatten()
|
||||
|
||||
{:ok, view}
|
||||
case Enum.find(views, fn {_name, id} -> id == lookup_id end) do
|
||||
{view_name, view_id} ->
|
||||
{:ok, %{id: view_id, name: "#{view_name}"}}
|
||||
|
||||
{:error, cause} ->
|
||||
{:error, cause}
|
||||
nil ->
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -136,7 +136,7 @@ defmodule Plausible.Google.UA.HTTP do
|
||||
url = "#{reporting_api_url()}/v4/reports:batchGet"
|
||||
headers = [{"Authorization", "Bearer #{access_token}"}]
|
||||
|
||||
case HTTPClient.post(url, headers, params) do
|
||||
case HTTPClient.impl().post(url, headers, params) do
|
||||
{:ok, %Finch.Response{body: body, status: 200}} ->
|
||||
report = List.first(body["reports"])
|
||||
|
||||
|
@ -750,16 +750,13 @@ defmodule PlausibleWeb.AuthController do
|
||||
def google_auth_callback(conn, %{"code" => code, "state" => state}) do
|
||||
res = Plausible.Google.API.fetch_access_token!(code)
|
||||
|
||||
[site_id, redirect_to, legacy, ga4] =
|
||||
[site_id, redirect_to, legacy] =
|
||||
case Jason.decode!(state) do
|
||||
[site_id, redirect_to] ->
|
||||
[site_id, redirect_to, true, false]
|
||||
[site_id, redirect_to, true]
|
||||
|
||||
[site_id, redirect_to, legacy] ->
|
||||
[site_id, redirect_to, legacy, false]
|
||||
|
||||
[site_id, redirect_to, legacy, ga4] ->
|
||||
[site_id, redirect_to, legacy, ga4]
|
||||
[site_id, redirect_to, legacy]
|
||||
end
|
||||
|
||||
site = Repo.get(Plausible.Site, site_id)
|
||||
@ -767,26 +764,15 @@ defmodule PlausibleWeb.AuthController do
|
||||
|
||||
case redirect_to do
|
||||
"import" ->
|
||||
if ga4 do
|
||||
redirect(conn,
|
||||
external:
|
||||
Routes.google_analytics4_path(conn, :property_form, site.domain,
|
||||
access_token: res["access_token"],
|
||||
refresh_token: res["refresh_token"],
|
||||
expires_at: NaiveDateTime.to_iso8601(expires_at)
|
||||
)
|
||||
)
|
||||
else
|
||||
redirect(conn,
|
||||
external:
|
||||
Routes.universal_analytics_path(conn, :view_id_form, site.domain,
|
||||
access_token: res["access_token"],
|
||||
refresh_token: res["refresh_token"],
|
||||
expires_at: NaiveDateTime.to_iso8601(expires_at),
|
||||
legacy: legacy
|
||||
)
|
||||
)
|
||||
end
|
||||
redirect(conn,
|
||||
external:
|
||||
Routes.google_analytics_path(conn, :property_or_view_form, site.domain,
|
||||
access_token: res["access_token"],
|
||||
refresh_token: res["refresh_token"],
|
||||
expires_at: NaiveDateTime.to_iso8601(expires_at),
|
||||
legacy: legacy
|
||||
)
|
||||
)
|
||||
|
||||
_ ->
|
||||
id_token = res["id_token"]
|
||||
|
@ -1,143 +0,0 @@
|
||||
defmodule PlausibleWeb.GoogleAnalytics4Controller do
|
||||
use PlausibleWeb, :controller
|
||||
|
||||
plug(PlausibleWeb.RequireAccountPlug)
|
||||
|
||||
plug(PlausibleWeb.AuthorizeSiteAccess, [:owner, :admin, :super_admin])
|
||||
|
||||
def property_form(conn, %{
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at
|
||||
}) do
|
||||
redirect_route = Routes.site_path(conn, :settings_imports_exports, conn.assigns.site.domain)
|
||||
|
||||
case Plausible.Google.GA4.API.list_properties(access_token) do
|
||||
{:ok, properties} ->
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("property_form.html",
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: conn.assigns.site,
|
||||
properties: properties,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
|
||||
{:error, :authentication_failed} ->
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"We were unable to authenticate your Google Analytics account. Please check that you have granted us permission to 'See and download your Google Analytics data' and try again."
|
||||
)
|
||||
|> redirect(external: redirect_route)
|
||||
|
||||
{:error, _any} ->
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"We were unable to list your Google Analytics properties. If the problem persists, please contact support for assistance."
|
||||
)
|
||||
|> redirect(external: redirect_route)
|
||||
end
|
||||
end
|
||||
|
||||
def property(conn, %{
|
||||
"property" => property,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
start_date = Plausible.Google.GA4.API.get_analytics_start_date(access_token, property)
|
||||
|
||||
case start_date do
|
||||
{:ok, nil} ->
|
||||
{:ok, properties} = Plausible.Google.GA4.API.list_properties(access_token)
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("property_form.html",
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: site,
|
||||
properties: properties,
|
||||
selected_property_error: "No data found. Nothing to import",
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
|
||||
{:ok, _date} ->
|
||||
redirect(conn,
|
||||
to:
|
||||
Routes.google_analytics4_path(conn, :confirm, site.domain,
|
||||
property: property,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def confirm(conn, %{
|
||||
"property" => property,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
|
||||
start_date = Plausible.Google.GA4.API.get_analytics_start_date(access_token, property)
|
||||
|
||||
end_date = Plausible.Sites.native_stats_start_date(site) || Timex.today(site.timezone)
|
||||
|
||||
{:ok, {property_name, property}} =
|
||||
Plausible.Google.GA4.API.get_property(access_token, property)
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("confirm.html",
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: site,
|
||||
selected_property: property,
|
||||
selected_property_name: property_name,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
end
|
||||
|
||||
def import(conn, %{
|
||||
"property" => property,
|
||||
"start_date" => start_date,
|
||||
"end_date" => end_date,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
current_user = conn.assigns.current_user
|
||||
|
||||
redirect_route = Routes.site_path(conn, :settings_imports_exports, site.domain)
|
||||
|
||||
{:ok, _} =
|
||||
Plausible.Imported.GoogleAnalytics4.new_import(
|
||||
site,
|
||||
current_user,
|
||||
property: property,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
token_expires_at: expires_at
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Import scheduled. An email will be sent when it completes.")
|
||||
|> redirect(external: redirect_route)
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
defmodule PlausibleWeb.GoogleAnalyticsController do
|
||||
use PlausibleWeb, :controller
|
||||
|
||||
plug(PlausibleWeb.RequireAccountPlug)
|
||||
@ -6,7 +6,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
plug(PlausibleWeb.AuthorizeSiteAccess, [:owner, :admin, :super_admin])
|
||||
|
||||
def user_metric_notice(conn, %{
|
||||
"view_id" => view_id,
|
||||
"property_or_view" => property_or_view,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at,
|
||||
@ -18,7 +18,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("user_metric_form.html",
|
||||
site: site,
|
||||
view_id: view_id,
|
||||
property_or_view: property_or_view,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
@ -27,29 +27,38 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
)
|
||||
end
|
||||
|
||||
def view_id_form(conn, %{
|
||||
def property_or_view_form(conn, %{
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at,
|
||||
"legacy" => legacy
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
|
||||
redirect_route =
|
||||
if legacy == "true" do
|
||||
Routes.site_path(conn, :settings_integrations, conn.assigns.site.domain)
|
||||
Routes.site_path(conn, :settings_integrations, site.domain)
|
||||
else
|
||||
Routes.site_path(conn, :settings_imports_exports, conn.assigns.site.domain)
|
||||
Routes.site_path(conn, :settings_imports_exports, site.domain)
|
||||
end
|
||||
|
||||
case Plausible.Google.UA.API.list_views(access_token) do
|
||||
{:ok, view_ids} ->
|
||||
result =
|
||||
if legacy == "true" do
|
||||
Plausible.Google.UA.API.list_views(access_token)
|
||||
else
|
||||
Plausible.Google.API.list_properties_and_views(access_token)
|
||||
end
|
||||
|
||||
case result do
|
||||
{:ok, properties_and_views} ->
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("view_id_form.html",
|
||||
|> render("property_or_view_form.html",
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: conn.assigns.site,
|
||||
view_ids: view_ids,
|
||||
properties_and_views: properties_and_views,
|
||||
legacy: legacy,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
@ -74,62 +83,62 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
|
||||
# see https://stackoverflow.com/a/57416769
|
||||
@google_analytics_new_user_metric_date ~D[2016-08-24]
|
||||
def view_id(conn, %{
|
||||
"view_id" => view_id,
|
||||
|
||||
def property_or_view(conn, %{
|
||||
"property_or_view" => property_or_view,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at,
|
||||
"legacy" => legacy
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
start_date = Plausible.Google.UA.API.get_analytics_start_date(access_token, view_id)
|
||||
start_date = Plausible.Google.API.get_analytics_start_date(access_token, property_or_view)
|
||||
|
||||
case start_date do
|
||||
{:ok, nil} ->
|
||||
{:ok, view_ids} = Plausible.Google.UA.API.list_views(access_token)
|
||||
{:ok, properties_and_views} =
|
||||
if legacy == "true" do
|
||||
Plausible.Google.UA.API.list_views(access_token)
|
||||
else
|
||||
Plausible.Google.API.list_properties_and_views(access_token)
|
||||
end
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("view_id_form.html",
|
||||
|> render("property_or_view_form.html",
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: site,
|
||||
view_ids: view_ids,
|
||||
selected_view_id_error: "No data found. Nothing to import",
|
||||
properties_and_views: properties_and_views,
|
||||
selected_property_or_view_error: "No data found. Nothing to import",
|
||||
legacy: legacy,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
|
||||
{:ok, date} ->
|
||||
if Timex.before?(date, @google_analytics_new_user_metric_date) do
|
||||
redirect(conn,
|
||||
to:
|
||||
Routes.universal_analytics_path(conn, :user_metric_notice, site.domain,
|
||||
view_id: view_id,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
legacy: legacy
|
||||
)
|
||||
)
|
||||
else
|
||||
redirect(conn,
|
||||
to:
|
||||
Routes.universal_analytics_path(conn, :confirm, site.domain,
|
||||
view_id: view_id,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
legacy: legacy
|
||||
)
|
||||
)
|
||||
end
|
||||
action =
|
||||
if Timex.before?(date, @google_analytics_new_user_metric_date) do
|
||||
:user_metric_notice
|
||||
else
|
||||
:confirm
|
||||
end
|
||||
|
||||
redirect(conn,
|
||||
to:
|
||||
Routes.google_analytics_path(conn, action, site.domain,
|
||||
property_or_view: property_or_view,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
legacy: legacy
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def confirm(conn, %{
|
||||
"view_id" => view_id,
|
||||
"property_or_view" => property_or_view,
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"expires_at" => expires_at,
|
||||
@ -137,11 +146,12 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
}) do
|
||||
site = conn.assigns.site
|
||||
|
||||
start_date = Plausible.Google.UA.API.get_analytics_start_date(access_token, view_id)
|
||||
start_date = Plausible.Google.API.get_analytics_start_date(access_token, property_or_view)
|
||||
|
||||
end_date = Plausible.Sites.native_stats_start_date(site) || Timex.today(site.timezone)
|
||||
|
||||
{:ok, {view_name, view_id}} = Plausible.Google.UA.API.get_view(access_token, view_id)
|
||||
{:ok, %{name: property_or_view_name, id: property_or_view}} =
|
||||
Plausible.Google.API.get_property_or_view(access_token, property_or_view)
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
@ -150,17 +160,18 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
refresh_token: refresh_token,
|
||||
expires_at: expires_at,
|
||||
site: site,
|
||||
selected_view_id: view_id,
|
||||
selected_view_id_name: view_name,
|
||||
selected_property_or_view: property_or_view,
|
||||
selected_property_or_view_name: property_or_view_name,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
property?: Plausible.Google.API.property?(property_or_view),
|
||||
legacy: legacy,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
end
|
||||
|
||||
def import(conn, %{
|
||||
"view_id" => view_id,
|
||||
"property_or_view" => property_or_view,
|
||||
"start_date" => start_date,
|
||||
"end_date" => end_date,
|
||||
"access_token" => access_token,
|
||||
@ -178,11 +189,23 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
Routes.site_path(conn, :settings_imports_exports, site.domain)
|
||||
end
|
||||
|
||||
{:ok, _} =
|
||||
if Plausible.Google.API.property?(property_or_view) do
|
||||
{:ok, _} =
|
||||
Plausible.Imported.GoogleAnalytics4.new_import(
|
||||
site,
|
||||
current_user,
|
||||
property: property_or_view,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
access_token: access_token,
|
||||
refresh_token: refresh_token,
|
||||
token_expires_at: expires_at
|
||||
)
|
||||
else
|
||||
Plausible.Imported.UniversalAnalytics.new_import(
|
||||
site,
|
||||
current_user,
|
||||
view_id: view_id,
|
||||
view_id: property_or_view,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
access_token: access_token,
|
||||
@ -190,6 +213,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||
token_expires_at: expires_at,
|
||||
legacy: legacy == "true"
|
||||
)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:success, "Import scheduled. An email will be sent when it completes.")
|
@ -373,27 +373,20 @@ defmodule PlausibleWeb.Router do
|
||||
delete "/:website", SiteController, :delete_site
|
||||
delete "/:website/stats", SiteController, :reset_stats
|
||||
|
||||
get "/:website/import/google-analytics/view-id",
|
||||
UniversalAnalyticsController,
|
||||
:view_id_form
|
||||
get "/:website/import/google-analytics/property-or-view",
|
||||
GoogleAnalyticsController,
|
||||
:property_or_view_form
|
||||
|
||||
post "/:website/import/google-analytics/view-id", UniversalAnalyticsController, :view_id
|
||||
post "/:website/import/google-analytics/property-or-view",
|
||||
GoogleAnalyticsController,
|
||||
:property_or_view
|
||||
|
||||
get "/:website/import/google-analytics/user-metric",
|
||||
UniversalAnalyticsController,
|
||||
GoogleAnalyticsController,
|
||||
:user_metric_notice
|
||||
|
||||
get "/:website/import/google-analytics/confirm", UniversalAnalyticsController, :confirm
|
||||
post "/:website/settings/google-import", UniversalAnalyticsController, :import
|
||||
|
||||
get "/:website/import/google-analytics4/property",
|
||||
GoogleAnalytics4Controller,
|
||||
:property_form
|
||||
|
||||
post "/:website/import/google-analytics4/property", GoogleAnalytics4Controller, :property
|
||||
|
||||
get "/:website/import/google-analytics4/confirm", GoogleAnalytics4Controller, :confirm
|
||||
post "/:website/settings/google4-import", GoogleAnalytics4Controller, :import
|
||||
get "/:website/import/google-analytics/confirm", GoogleAnalyticsController, :confirm
|
||||
post "/:website/settings/google-import", GoogleAnalyticsController, :import
|
||||
|
||||
delete "/:website/settings/forget-imported", SiteController, :forget_imported
|
||||
delete "/:website/settings/forget-import/:import_id", SiteController, :forget_import
|
||||
|
@ -1,4 +1,4 @@
|
||||
<%= form_for @conn, Routes.universal_analytics_path(@conn, :import, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<%= form_for @conn, Routes.google_analytics_path(@conn, :import, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Import from Google Analytics</h2>
|
||||
|
||||
<%= hidden_input(f, :access_token, value: @access_token) %>
|
||||
@ -9,15 +9,28 @@
|
||||
<%= case @start_date do %>
|
||||
<% {:ok, start_date} -> %>
|
||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
||||
Stats from this property and time period will be imported from your Google Analytics account to your Plausible dashboard
|
||||
Stats from this
|
||||
<%= if @property? do %>
|
||||
property
|
||||
<% else %>
|
||||
view
|
||||
<% end %>
|
||||
and time period will be imported from your Google Analytics account to your Plausible dashboard
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<%= styled_label(f, :view_id, "Google Analytics view") %>
|
||||
<%= styled_label(
|
||||
f,
|
||||
:property_or_view,
|
||||
"Google Analytics #{if @property?, do: "property", else: "view"}"
|
||||
) %>
|
||||
<span class="block w-full text-base dark:text-gray-100 sm:text-sm dark:bg-gray-800">
|
||||
<%= @selected_view_id_name %>
|
||||
<%= @selected_property_or_view_name %>
|
||||
</span>
|
||||
<%= hidden_input(f, :view_id, readonly: "true", value: @selected_view_id) %>
|
||||
<%= hidden_input(f, :property_or_view,
|
||||
readonly: "true",
|
||||
value: @selected_property_or_view
|
||||
) %>
|
||||
</div>
|
||||
<div class="flex justify-between mt-3">
|
||||
<div class="w-36">
|
@ -0,0 +1,23 @@
|
||||
<%= form_for @conn, Routes.google_analytics_path(@conn, :property_or_view, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Import from Google Analytics</h2>
|
||||
|
||||
<%= hidden_input(f, :access_token, value: @access_token) %>
|
||||
<%= hidden_input(f, :refresh_token, value: @refresh_token) %>
|
||||
<%= hidden_input(f, :expires_at, value: @expires_at) %>
|
||||
<%= hidden_input(f, :legacy, value: @legacy) %>
|
||||
|
||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
||||
Choose the property or view in your Google Analytics account that will be imported to the <%= @site.domain %> dashboard.
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<%= styled_label(f, :property_or_view, "Google Analytics property or view") %>
|
||||
<%= styled_select(f, :property_or_view, @properties_and_views,
|
||||
prompt: "(Choose property or view)",
|
||||
required: "true"
|
||||
) %>
|
||||
<%= styled_error(@conn.assigns[:selected_property_or_view_error]) %>
|
||||
</div>
|
||||
|
||||
<%= submit("Continue ->", class: "button mt-6") %>
|
||||
<% end %>
|
@ -34,8 +34,8 @@
|
||||
|
||||
<%= link("Continue ->",
|
||||
to:
|
||||
Routes.universal_analytics_path(@conn, :confirm, @site.domain,
|
||||
view_id: @view_id,
|
||||
Routes.google_analytics_path(@conn, :confirm, @site.domain,
|
||||
property_or_view: @property_or_view,
|
||||
access_token: @access_token,
|
||||
refresh_token: @refresh_token,
|
||||
expires_at: @expires_at,
|
@ -1,46 +0,0 @@
|
||||
<%= form_for @conn, Routes.google_analytics4_path(@conn, :import, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Import from Google Analytics</h2>
|
||||
|
||||
<%= hidden_input(f, :access_token, value: @access_token) %>
|
||||
<%= hidden_input(f, :refresh_token, value: @refresh_token) %>
|
||||
<%= hidden_input(f, :expires_at, value: @expires_at) %>
|
||||
|
||||
<%= case @start_date do %>
|
||||
<% {:ok, start_date} -> %>
|
||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
||||
Stats from this property and time period will be imported from your Google Analytics 4 account to your Plausible dashboard
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<%= styled_label(f, :property, "Google Analytics 4 property") %>
|
||||
<span class="block w-full text-base dark:text-gray-100 sm:text-sm dark:bg-gray-800">
|
||||
<%= @selected_property_name %>
|
||||
</span>
|
||||
<%= hidden_input(f, :property, readonly: "true", value: @selected_property) %>
|
||||
</div>
|
||||
<div class="flex justify-between mt-3">
|
||||
<div class="w-36">
|
||||
<%= styled_label(f, :start_date, "From") %>
|
||||
<span class="block w-full text-base dark:text-gray-100 sm:text-sm dark:bg-gray-800">
|
||||
<%= PlausibleWeb.EmailView.date_format(start_date) %>
|
||||
</span>
|
||||
<%= hidden_input(f, :start_date, value: start_date, readonly: "true") %>
|
||||
</div>
|
||||
<div class="align-middle pt-4 dark:text-gray-100">→</div>
|
||||
<div class="w-36">
|
||||
<%= styled_label(f, :end_date, "To") %>
|
||||
<span class="block w-full text-base dark:text-gray-100 sm:text-sm dark:bg-gray-800">
|
||||
<%= PlausibleWeb.EmailView.date_format(@end_date) %>
|
||||
</span>
|
||||
<%= hidden_input(f, :end_date, value: @end_date, readonly: "true") %>
|
||||
</div>
|
||||
</div>
|
||||
<% {:error, error} -> %>
|
||||
<p class="text-gray-700 dark:text-gray-300 mt-6">
|
||||
The following error occurred when fetching your Google Analytics 4 data.
|
||||
</p>
|
||||
<p class="text-red-700 font-medium mt-3"><%= error %></p>
|
||||
<% end %>
|
||||
|
||||
<%= submit("Confirm import", class: "button mt-6") %>
|
||||
<% end %>
|
@ -1,19 +0,0 @@
|
||||
<%= form_for @conn, Routes.google_analytics4_path(@conn, :property, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Import from Google Analytics 4</h2>
|
||||
|
||||
<%= hidden_input(f, :access_token, value: @access_token) %>
|
||||
<%= hidden_input(f, :refresh_token, value: @refresh_token) %>
|
||||
<%= hidden_input(f, :expires_at, value: @expires_at) %>
|
||||
|
||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
||||
Choose the property in your Google Analytics 4 account that will be imported to the <%= @site.domain %> dashboard.
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<%= styled_label(f, :property, "Google Analytics 4 property") %>
|
||||
<%= styled_select(f, :property, @properties, prompt: "(Choose property)", required: "true") %>
|
||||
<%= styled_error(@conn.assigns[:selected_property_error]) %>
|
||||
</div>
|
||||
|
||||
<%= submit("Continue ->", class: "button mt-6") %>
|
||||
<% end %>
|
@ -16,21 +16,7 @@
|
||||
theme="bright"
|
||||
href={Plausible.Google.API.import_authorize_url(@site.id, "import", legacy: false)}
|
||||
>
|
||||
<img src="/images/icon/universal_analytics_logo.svg" alt="New Universal Analytics import" />
|
||||
</PlausibleWeb.Components.Generic.button_link>
|
||||
|
||||
<PlausibleWeb.Components.Generic.button_link
|
||||
class="w-36 h-20"
|
||||
theme="bright"
|
||||
href={
|
||||
Plausible.Google.API.import_authorize_url(@site.id, "import", legacy: false, ga4: true)
|
||||
}
|
||||
>
|
||||
<img
|
||||
src="/images/icon/google_analytics_4_logo.svg"
|
||||
width="110"
|
||||
alt="New Google Analytics 4 import"
|
||||
/>
|
||||
<img src="/images/icon/google_analytics_logo.svg" alt="Google Analytics import" />
|
||||
</PlausibleWeb.Components.Generic.button_link>
|
||||
|
||||
<PlausibleWeb.Components.Generic.button_link
|
||||
|
@ -1,20 +0,0 @@
|
||||
<%= form_for @conn, Routes.universal_analytics_path(@conn, :view_id, @site.domain), [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Import from Google Analytics</h2>
|
||||
|
||||
<%= hidden_input(f, :access_token, value: @access_token) %>
|
||||
<%= hidden_input(f, :refresh_token, value: @refresh_token) %>
|
||||
<%= hidden_input(f, :expires_at, value: @expires_at) %>
|
||||
<%= hidden_input(f, :legacy, value: @legacy) %>
|
||||
|
||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
||||
Choose the view in your Google Analytics account that will be imported to the <%= @site.domain %> dashboard.
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<%= styled_label(f, :view_id, "Google Analytics view") %>
|
||||
<%= styled_select(f, :view_id, @view_ids, prompt: "(Choose view)", required: "true") %>
|
||||
<%= styled_error(@conn.assigns[:selected_view_id_error]) %>
|
||||
</div>
|
||||
|
||||
<%= submit("Continue ->", class: "button mt-6") %>
|
||||
<% end %>
|
@ -1,4 +0,0 @@
|
||||
defmodule PlausibleWeb.GoogleAnalytics4View do
|
||||
use PlausibleWeb, :view
|
||||
use Plausible
|
||||
end
|
4
lib/plausible_web/views/google_analytics_view.ex
Normal file
4
lib/plausible_web/views/google_analytics_view.ex
Normal file
@ -0,0 +1,4 @@
|
||||
defmodule PlausibleWeb.GoogleAnalyticsView do
|
||||
use PlausibleWeb, :view
|
||||
use Plausible
|
||||
end
|
@ -1,4 +0,0 @@
|
||||
defmodule PlausibleWeb.UniversalAnalyticsView do
|
||||
use PlausibleWeb, :view
|
||||
use Plausible
|
||||
end
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@ -390,10 +390,10 @@ defmodule Plausible.Google.APITest do
|
||||
)
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
"one.test" => [{"57238190 - one.test", "57238190"}],
|
||||
"two.test" => [{"54460083 - two.test", "54460083"}]
|
||||
}} == Google.UA.API.list_views("access_token")
|
||||
[
|
||||
{"one.test", [{"57238190 - one.test", "57238190"}]},
|
||||
{"two.test", [{"54460083 - two.test", "54460083"}]}
|
||||
]} == Google.UA.API.list_views("access_token")
|
||||
end
|
||||
|
||||
test "list_views/1 returns authentication_failed when request fails with HTTP 403" do
|
||||
|
@ -31,14 +31,15 @@ defmodule Plausible.Google.GA4.APITest do
|
||||
|
||||
describe "get_property/2" do
|
||||
test "returns tuple consisting of display name and value of a property" do
|
||||
result = Jason.decode!(File.read!("fixture/ga4_list_properties.json"))
|
||||
result = Jason.decode!(File.read!("fixture/ga4_get_property.json"))
|
||||
|
||||
expect(Plausible.HTTPClient.Mock, :get, fn _url, _opts ->
|
||||
{:ok, %Finch.Response{status: 200, body: result}}
|
||||
end)
|
||||
|
||||
assert {:ok, {"GA4 - Flood-It! (properties/153293282)", "properties/153293282"}} =
|
||||
GA4.API.get_property("some_access_token", "properties/153293282")
|
||||
assert {:ok,
|
||||
%{name: "account.one - GA4 (properties/428685444)", id: "properties/428685444"}} =
|
||||
GA4.API.get_property("some_access_token", "properties/428685444")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,336 @@
|
||||
defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
|
||||
use PlausibleWeb.ConnCase, async: false
|
||||
use Oban.Testing, repo: Plausible.Repo
|
||||
|
||||
import Mox
|
||||
import Plausible.Test.Support.HTML
|
||||
|
||||
alias Plausible.Imported.SiteImport
|
||||
|
||||
require Plausible.Imported.SiteImport
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "GET /:website/import/google-analytics/user-metric" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "renders with link to confirmation page", %{conn: conn, site: site} do
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/user-metric", %{
|
||||
"property_or_view" => "123456",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~
|
||||
PlausibleWeb.Router.Helpers.google_analytics_path(conn, :confirm, site.domain,
|
||||
property_or_view: "123456",
|
||||
access_token: "token",
|
||||
refresh_token: "foo",
|
||||
expires_at: "2022-09-22T20:01:37.112777",
|
||||
legacy: "true"
|
||||
)
|
||||
|> String.replace("&", "&")
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /:website/import/google-analytics/property-or-view" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "lists Google Analytics views (legacy)", %{conn: conn, site: site} do
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _opts ->
|
||||
body = "fixture/ga_list_views.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~ "57238190 - one.test"
|
||||
assert response =~ "54460083 - two.test"
|
||||
end
|
||||
|
||||
test "lists Google Analytics views and properties", %{conn: conn, site: site} do
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _opts ->
|
||||
body = "fixture/ga4_list_properties.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _opts ->
|
||||
body = "fixture/ga_list_views.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "false"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~ "57238190 - one.test"
|
||||
assert response =~ "54460083 - two.test"
|
||||
assert response =~ "account.one - GA4 (properties/428685906)"
|
||||
assert response =~ "GA4 - Flood-It! (properties/153293282)"
|
||||
assert response =~ "GA4 - Google Merch Shop (properties/213025502)"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /:website/import/google-analytics/confirm" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "renders confirmation form for Universal Analytics import", %{conn: conn, site: site} do
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:post,
|
||||
fn _url, _headers, _params ->
|
||||
body = "fixture/ga_start_date.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _headers ->
|
||||
body = "fixture/ga_list_views.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/confirm", %{
|
||||
"property_or_view" => "57238190",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
action_url = PlausibleWeb.Router.Helpers.google_analytics_path(conn, :import, site.domain)
|
||||
|
||||
assert text_of_attr(response, "form", "action") == action_url
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=access_token]|, "value") == "token"
|
||||
assert text_of_attr(response, ~s|input[name=refresh_token]|, "value") == "foo"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=expires_at]|, "value") ==
|
||||
"2022-09-22T20:01:37.112777"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=legacy]|, "value") == "true"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=property_or_view]|, "value") == "57238190"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=start_date]|, "value") == "2012-01-18"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=end_date]|, "value") ==
|
||||
Date.to_iso8601(Date.utc_today())
|
||||
end
|
||||
|
||||
test "renders confirmation form for Google Analytics 4 import", %{conn: conn, site: site} do
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:post,
|
||||
fn _url, _headers, _params ->
|
||||
body = "fixture/ga4_start_date.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _headers ->
|
||||
body = "fixture/ga4_get_property.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/confirm", %{
|
||||
"property_or_view" => "properties/428685444",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
action_url = PlausibleWeb.Router.Helpers.google_analytics_path(conn, :import, site.domain)
|
||||
|
||||
assert text_of_attr(response, "form", "action") == action_url
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=access_token]|, "value") == "token"
|
||||
assert text_of_attr(response, ~s|input[name=refresh_token]|, "value") == "foo"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=expires_at]|, "value") ==
|
||||
"2022-09-22T20:01:37.112777"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=legacy]|, "value") == "true"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=property_or_view]|, "value") ==
|
||||
"properties/428685444"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=start_date]|, "value") == "2024-02-22"
|
||||
|
||||
assert text_of_attr(response, ~s|input[name=end_date]|, "value") ==
|
||||
Date.to_iso8601(Date.utc_today())
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /:website/settings/google-import" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "creates Google Analytics 4 site import instance", %{conn: conn, site: site} do
|
||||
conn =
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"property_or_view" => "properties/123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "false"
|
||||
})
|
||||
|
||||
assert redirected_to(conn, 302) ==
|
||||
PlausibleWeb.Router.Helpers.site_path(conn, :settings_imports_exports, site.domain)
|
||||
|
||||
[site_import] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert site_import.source == :google_analytics_4
|
||||
assert site_import.end_date == ~D[2022-03-01]
|
||||
assert site_import.status == SiteImport.pending()
|
||||
end
|
||||
|
||||
test "creates Universal Analytics site import instance", %{conn: conn, site: site} do
|
||||
conn =
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"property_or_view" => "123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|
||||
assert redirected_to(conn, 302) ==
|
||||
PlausibleWeb.Router.Helpers.site_path(conn, :settings_integrations, site.domain)
|
||||
|
||||
[site_import] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert site_import.source == :universal_analytics
|
||||
assert site_import.end_date == ~D[2022-03-01]
|
||||
assert site_import.status == SiteImport.pending()
|
||||
end
|
||||
|
||||
test "redirects to imports and exports when creating UA job with legacy set to false", %{
|
||||
conn: conn,
|
||||
site: site
|
||||
} do
|
||||
conn =
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"property_or_view" => "123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "false"
|
||||
})
|
||||
|
||||
assert redirected_to(conn, 302) ==
|
||||
PlausibleWeb.Router.Helpers.site_path(conn, :settings_imports_exports, site.domain)
|
||||
|
||||
[site_import] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert site_import.source == :universal_analytics
|
||||
assert site_import.end_date == ~D[2022-03-01]
|
||||
assert site_import.status == SiteImport.pending()
|
||||
end
|
||||
|
||||
test "schedules a Google Analytics 4 import job in Oban", %{conn: conn, site: site} do
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"property_or_view" => "properties/123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "false"
|
||||
})
|
||||
|
||||
assert [%{id: import_id, legacy: false}] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Plausible.Workers.ImportAnalytics,
|
||||
args: %{
|
||||
"import_id" => import_id,
|
||||
"property" => "properties/123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"token_expires_at" => "2022-09-22T20:01:37.112777"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
test "schedules a Universal Analytics import job in Oban", %{conn: conn, site: site} do
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"property_or_view" => "123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|
||||
assert [%{id: import_id, legacy: true}] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Plausible.Workers.ImportAnalytics,
|
||||
args: %{
|
||||
"import_id" => import_id,
|
||||
"view_id" => "123456",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"token_expires_at" => "2022-09-22T20:01:37.112777"
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
@ -1274,83 +1274,6 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /:website/import/google-analytics/view-id" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "lists Google Analytics views", %{conn: conn, site: site} do
|
||||
expect(
|
||||
Plausible.HTTPClient.Mock,
|
||||
:get,
|
||||
fn _url, _body ->
|
||||
body = "fixture/ga_list_views.json" |> File.read!() |> Jason.decode!()
|
||||
{:ok, %Finch.Response{body: body, status: 200}}
|
||||
end
|
||||
)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/#{site.domain}/import/google-analytics/view-id", %{
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~ "57238190 - one.test"
|
||||
assert response =~ "54460083 - two.test"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /:website/settings/google-import" do
|
||||
setup [:create_user, :log_in, :create_new_site]
|
||||
|
||||
test "creates site import instance", %{conn: conn, site: site} do
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"view_id" => "123",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|
||||
[site_import] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert site_import.source == :universal_analytics
|
||||
assert site_import.end_date == ~D[2022-03-01]
|
||||
assert site_import.status == SiteImport.pending()
|
||||
end
|
||||
|
||||
test "schedules an import job in Oban", %{conn: conn, site: site} do
|
||||
post(conn, "/#{site.domain}/settings/google-import", %{
|
||||
"view_id" => "123",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"expires_at" => "2022-09-22T20:01:37.112777",
|
||||
"legacy" => "true"
|
||||
})
|
||||
|
||||
assert [%{id: import_id, legacy: true}] = Plausible.Imported.list_all_imports(site)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Plausible.Workers.ImportAnalytics,
|
||||
args: %{
|
||||
"import_id" => import_id,
|
||||
"view_id" => "123",
|
||||
"start_date" => "2018-03-01",
|
||||
"end_date" => "2022-03-01",
|
||||
"access_token" => "token",
|
||||
"refresh_token" => "foo",
|
||||
"token_expires_at" => "2022-09-22T20:01:37.112777"
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /:website/settings/:forget_import/:import_id" do
|
||||
setup [:create_user, :log_in, :create_new_site, :add_imported_data]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user