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
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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
|
def import_authorize_url(site_id, redirect_to, opts \\ []) do
|
||||||
legacy = Keyword.get(opts, :legacy, true)
|
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=" <>
|
"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
|
end
|
||||||
|
|
||||||
def fetch_access_token!(code) do
|
def fetch_access_token!(code) do
|
||||||
HTTP.fetch_access_token!(code)
|
HTTP.fetch_access_token!(code)
|
||||||
end
|
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
|
def fetch_verified_properties(auth) do
|
||||||
with {:ok, access_token} <- maybe_refresh_token(auth),
|
with {:ok, access_token} <- maybe_refresh_token(auth),
|
||||||
{:ok, sites} <- Plausible.Google.HTTP.list_sites(access_token) do
|
{:ok, sites} <- Plausible.Google.HTTP.list_sites(access_token) do
|
||||||
|
|
@ -90,6 +112,8 @@ defmodule Plausible.Google.API do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def property?(value), do: String.starts_with?(value, "properties/")
|
||||||
|
|
||||||
defp do_refresh_token(refresh_token) do
|
defp do_refresh_token(refresh_token) do
|
||||||
case HTTP.refresh_auth_token(refresh_token) do
|
case HTTP.refresh_auth_token(refresh_token) do
|
||||||
{:ok, %{"access_token" => new_access_token, "expires_in" => expires_in}} ->
|
{:ok, %{"access_token" => new_access_token, "expires_in" => expires_in}} ->
|
||||||
|
|
|
||||||
|
|
@ -25,29 +25,35 @@ defmodule Plausible.Google.GA4.API do
|
||||||
accounts
|
accounts
|
||||||
|> Enum.filter(& &1["propertySummaries"])
|
|> Enum.filter(& &1["propertySummaries"])
|
||||||
|> Enum.map(fn account ->
|
|> 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 ->
|
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)}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, accounts}
|
{:ok, accounts}
|
||||||
|
|
||||||
error ->
|
{:error, cause} ->
|
||||||
error
|
{:error, cause}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_property(access_token, lookup_property) do
|
def get_property(access_token, lookup_property) do
|
||||||
case list_properties(access_token) do
|
case GA4.HTTP.get_property(access_token, lookup_property) do
|
||||||
{:ok, properties} ->
|
{:ok, property} ->
|
||||||
property =
|
%{"displayName" => property_name, "name" => property_id, "account" => account_id} =
|
||||||
properties
|
property
|
||||||
|> Enum.map(&elem(&1, 1))
|
|
||||||
|> List.flatten()
|
|
||||||
|> Enum.find(fn {_name, property} -> property == lookup_property end)
|
|
||||||
|
|
||||||
{:ok, property}
|
{:ok,
|
||||||
|
%{
|
||||||
|
id: property_id,
|
||||||
|
name: "#{property_name} (#{property_id})",
|
||||||
|
account_id: account_id
|
||||||
|
}}
|
||||||
|
|
||||||
{:error, cause} ->
|
{:error, cause} ->
|
||||||
{:error, cause}
|
{:error, cause}
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,8 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||||
)
|
)
|
||||||
|
|
||||||
with {:ok, %{body: body}} <- response,
|
with {:ok, %{body: body}} <- response,
|
||||||
# File.write!("fixture/ga4_report_#{report_request.dataset}.json", Jason.encode!(body)),
|
|
||||||
{:ok, report} <- parse_report_from_response(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} <- convert_to_maps(report) do
|
||||||
{:ok, {report, row_count}}
|
{:ok, {report, row_count}}
|
||||||
else
|
else
|
||||||
|
|
@ -130,7 +129,7 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_accounts_for_user(access_token) do
|
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}"}]
|
headers = [{"Authorization", "Bearer #{access_token}"}]
|
||||||
|
|
||||||
|
|
@ -147,6 +146,30 @@ defmodule Plausible.Google.GA4.HTTP do
|
||||||
end
|
end
|
||||||
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"
|
@earliest_valid_date "2015-08-14"
|
||||||
def get_analytics_start_date(access_token, property) do
|
def get_analytics_start_date(access_token, property) do
|
||||||
params = %{
|
params = %{
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,11 @@ defmodule Plausible.Google.UA.API do
|
||||||
def list_views(access_token) do
|
def list_views(access_token) do
|
||||||
case UA.HTTP.list_views_for_user(access_token) do
|
case UA.HTTP.list_views_for_user(access_token) do
|
||||||
{:ok, %{"items" => views}} ->
|
{: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}
|
{:ok, views}
|
||||||
|
|
||||||
error ->
|
error ->
|
||||||
|
|
@ -40,18 +44,19 @@ defmodule Plausible.Google.UA.API do
|
||||||
Returns a single Google Analytics view if the user has access to it.
|
Returns a single Google Analytics view if the user has access to it.
|
||||||
"""
|
"""
|
||||||
def get_view(access_token, lookup_id) do
|
def get_view(access_token, lookup_id) do
|
||||||
case list_views(access_token) do
|
with {:ok, views} <- list_views(access_token) do
|
||||||
{:ok, views} ->
|
views =
|
||||||
view =
|
|
||||||
views
|
views
|
||||||
|> Map.values()
|
|> Enum.map(&elem(&1, 1))
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|> Enum.find(fn {_name, id} -> id == lookup_id end)
|
|
||||||
|
|
||||||
{: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} ->
|
nil ->
|
||||||
{:error, cause}
|
{:error, :not_found}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ defmodule Plausible.Google.UA.HTTP do
|
||||||
url = "#{reporting_api_url()}/v4/reports:batchGet"
|
url = "#{reporting_api_url()}/v4/reports:batchGet"
|
||||||
headers = [{"Authorization", "Bearer #{access_token}"}]
|
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}} ->
|
{:ok, %Finch.Response{body: body, status: 200}} ->
|
||||||
report = List.first(body["reports"])
|
report = List.first(body["reports"])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -750,16 +750,13 @@ defmodule PlausibleWeb.AuthController do
|
||||||
def google_auth_callback(conn, %{"code" => code, "state" => state}) do
|
def google_auth_callback(conn, %{"code" => code, "state" => state}) do
|
||||||
res = Plausible.Google.API.fetch_access_token!(code)
|
res = Plausible.Google.API.fetch_access_token!(code)
|
||||||
|
|
||||||
[site_id, redirect_to, legacy, ga4] =
|
[site_id, redirect_to, legacy] =
|
||||||
case Jason.decode!(state) do
|
case Jason.decode!(state) do
|
||||||
[site_id, redirect_to] ->
|
[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] ->
|
||||||
[site_id, redirect_to, legacy, false]
|
[site_id, redirect_to, legacy]
|
||||||
|
|
||||||
[site_id, redirect_to, legacy, ga4] ->
|
|
||||||
[site_id, redirect_to, legacy, ga4]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
site = Repo.get(Plausible.Site, site_id)
|
site = Repo.get(Plausible.Site, site_id)
|
||||||
|
|
@ -767,26 +764,15 @@ defmodule PlausibleWeb.AuthController do
|
||||||
|
|
||||||
case redirect_to do
|
case redirect_to do
|
||||||
"import" ->
|
"import" ->
|
||||||
if ga4 do
|
|
||||||
redirect(conn,
|
redirect(conn,
|
||||||
external:
|
external:
|
||||||
Routes.google_analytics4_path(conn, :property_form, site.domain,
|
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)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else
|
|
||||||
redirect(conn,
|
|
||||||
external:
|
|
||||||
Routes.universal_analytics_path(conn, :view_id_form, site.domain,
|
|
||||||
access_token: res["access_token"],
|
access_token: res["access_token"],
|
||||||
refresh_token: res["refresh_token"],
|
refresh_token: res["refresh_token"],
|
||||||
expires_at: NaiveDateTime.to_iso8601(expires_at),
|
expires_at: NaiveDateTime.to_iso8601(expires_at),
|
||||||
legacy: legacy
|
legacy: legacy
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
id_token = res["id_token"]
|
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
|
use PlausibleWeb, :controller
|
||||||
|
|
||||||
plug(PlausibleWeb.RequireAccountPlug)
|
plug(PlausibleWeb.RequireAccountPlug)
|
||||||
|
|
@ -6,7 +6,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
plug(PlausibleWeb.AuthorizeSiteAccess, [:owner, :admin, :super_admin])
|
plug(PlausibleWeb.AuthorizeSiteAccess, [:owner, :admin, :super_admin])
|
||||||
|
|
||||||
def user_metric_notice(conn, %{
|
def user_metric_notice(conn, %{
|
||||||
"view_id" => view_id,
|
"property_or_view" => property_or_view,
|
||||||
"access_token" => access_token,
|
"access_token" => access_token,
|
||||||
"refresh_token" => refresh_token,
|
"refresh_token" => refresh_token,
|
||||||
"expires_at" => expires_at,
|
"expires_at" => expires_at,
|
||||||
|
|
@ -18,7 +18,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
|> assign(:skip_plausible_tracking, true)
|
|> assign(:skip_plausible_tracking, true)
|
||||||
|> render("user_metric_form.html",
|
|> render("user_metric_form.html",
|
||||||
site: site,
|
site: site,
|
||||||
view_id: view_id,
|
property_or_view: property_or_view,
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
|
|
@ -27,29 +27,38 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def view_id_form(conn, %{
|
def property_or_view_form(conn, %{
|
||||||
"access_token" => access_token,
|
"access_token" => access_token,
|
||||||
"refresh_token" => refresh_token,
|
"refresh_token" => refresh_token,
|
||||||
"expires_at" => expires_at,
|
"expires_at" => expires_at,
|
||||||
"legacy" => legacy
|
"legacy" => legacy
|
||||||
}) do
|
}) do
|
||||||
|
site = conn.assigns.site
|
||||||
|
|
||||||
redirect_route =
|
redirect_route =
|
||||||
if legacy == "true" do
|
if legacy == "true" do
|
||||||
Routes.site_path(conn, :settings_integrations, conn.assigns.site.domain)
|
Routes.site_path(conn, :settings_integrations, site.domain)
|
||||||
else
|
else
|
||||||
Routes.site_path(conn, :settings_imports_exports, conn.assigns.site.domain)
|
Routes.site_path(conn, :settings_imports_exports, site.domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
case Plausible.Google.UA.API.list_views(access_token) do
|
result =
|
||||||
{:ok, view_ids} ->
|
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
|
conn
|
||||||
|> assign(:skip_plausible_tracking, true)
|
|> assign(:skip_plausible_tracking, true)
|
||||||
|> render("view_id_form.html",
|
|> render("property_or_view_form.html",
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
site: conn.assigns.site,
|
site: conn.assigns.site,
|
||||||
view_ids: view_ids,
|
properties_and_views: properties_and_views,
|
||||||
legacy: legacy,
|
legacy: legacy,
|
||||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||||
)
|
)
|
||||||
|
|
@ -74,50 +83,51 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
|
|
||||||
# see https://stackoverflow.com/a/57416769
|
# see https://stackoverflow.com/a/57416769
|
||||||
@google_analytics_new_user_metric_date ~D[2016-08-24]
|
@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,
|
"access_token" => access_token,
|
||||||
"refresh_token" => refresh_token,
|
"refresh_token" => refresh_token,
|
||||||
"expires_at" => expires_at,
|
"expires_at" => expires_at,
|
||||||
"legacy" => legacy
|
"legacy" => legacy
|
||||||
}) do
|
}) do
|
||||||
site = conn.assigns.site
|
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
|
case start_date do
|
||||||
{:ok, nil} ->
|
{: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
|
conn
|
||||||
|> assign(:skip_plausible_tracking, true)
|
|> assign(:skip_plausible_tracking, true)
|
||||||
|> render("view_id_form.html",
|
|> render("property_or_view_form.html",
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
site: site,
|
site: site,
|
||||||
view_ids: view_ids,
|
properties_and_views: properties_and_views,
|
||||||
selected_view_id_error: "No data found. Nothing to import",
|
selected_property_or_view_error: "No data found. Nothing to import",
|
||||||
legacy: legacy,
|
legacy: legacy,
|
||||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, date} ->
|
{:ok, date} ->
|
||||||
|
action =
|
||||||
if Timex.before?(date, @google_analytics_new_user_metric_date) do
|
if Timex.before?(date, @google_analytics_new_user_metric_date) do
|
||||||
redirect(conn,
|
:user_metric_notice
|
||||||
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
|
else
|
||||||
|
:confirm
|
||||||
|
end
|
||||||
|
|
||||||
redirect(conn,
|
redirect(conn,
|
||||||
to:
|
to:
|
||||||
Routes.universal_analytics_path(conn, :confirm, site.domain,
|
Routes.google_analytics_path(conn, action, site.domain,
|
||||||
view_id: view_id,
|
property_or_view: property_or_view,
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
|
|
@ -126,10 +136,9 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def confirm(conn, %{
|
def confirm(conn, %{
|
||||||
"view_id" => view_id,
|
"property_or_view" => property_or_view,
|
||||||
"access_token" => access_token,
|
"access_token" => access_token,
|
||||||
"refresh_token" => refresh_token,
|
"refresh_token" => refresh_token,
|
||||||
"expires_at" => expires_at,
|
"expires_at" => expires_at,
|
||||||
|
|
@ -137,11 +146,12 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
}) do
|
}) do
|
||||||
site = conn.assigns.site
|
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)
|
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
|
conn
|
||||||
|> assign(:skip_plausible_tracking, true)
|
|> assign(:skip_plausible_tracking, true)
|
||||||
|
|
@ -150,17 +160,18 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
refresh_token: refresh_token,
|
refresh_token: refresh_token,
|
||||||
expires_at: expires_at,
|
expires_at: expires_at,
|
||||||
site: site,
|
site: site,
|
||||||
selected_view_id: view_id,
|
selected_property_or_view: property_or_view,
|
||||||
selected_view_id_name: view_name,
|
selected_property_or_view_name: property_or_view_name,
|
||||||
start_date: start_date,
|
start_date: start_date,
|
||||||
end_date: end_date,
|
end_date: end_date,
|
||||||
|
property?: Plausible.Google.API.property?(property_or_view),
|
||||||
legacy: legacy,
|
legacy: legacy,
|
||||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import(conn, %{
|
def import(conn, %{
|
||||||
"view_id" => view_id,
|
"property_or_view" => property_or_view,
|
||||||
"start_date" => start_date,
|
"start_date" => start_date,
|
||||||
"end_date" => end_date,
|
"end_date" => end_date,
|
||||||
"access_token" => access_token,
|
"access_token" => access_token,
|
||||||
|
|
@ -178,11 +189,23 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
Routes.site_path(conn, :settings_imports_exports, site.domain)
|
Routes.site_path(conn, :settings_imports_exports, site.domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Plausible.Google.API.property?(property_or_view) do
|
||||||
{:ok, _} =
|
{: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(
|
Plausible.Imported.UniversalAnalytics.new_import(
|
||||||
site,
|
site,
|
||||||
current_user,
|
current_user,
|
||||||
view_id: view_id,
|
view_id: property_or_view,
|
||||||
start_date: start_date,
|
start_date: start_date,
|
||||||
end_date: end_date,
|
end_date: end_date,
|
||||||
access_token: access_token,
|
access_token: access_token,
|
||||||
|
|
@ -190,6 +213,7 @@ defmodule PlausibleWeb.UniversalAnalyticsController do
|
||||||
token_expires_at: expires_at,
|
token_expires_at: expires_at,
|
||||||
legacy: legacy == "true"
|
legacy: legacy == "true"
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:success, "Import scheduled. An email will be sent when it completes.")
|
|> 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", SiteController, :delete_site
|
||||||
delete "/:website/stats", SiteController, :reset_stats
|
delete "/:website/stats", SiteController, :reset_stats
|
||||||
|
|
||||||
get "/:website/import/google-analytics/view-id",
|
get "/:website/import/google-analytics/property-or-view",
|
||||||
UniversalAnalyticsController,
|
GoogleAnalyticsController,
|
||||||
:view_id_form
|
: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",
|
get "/:website/import/google-analytics/user-metric",
|
||||||
UniversalAnalyticsController,
|
GoogleAnalyticsController,
|
||||||
:user_metric_notice
|
:user_metric_notice
|
||||||
|
|
||||||
get "/:website/import/google-analytics/confirm", UniversalAnalyticsController, :confirm
|
get "/:website/import/google-analytics/confirm", GoogleAnalyticsController, :confirm
|
||||||
post "/:website/settings/google-import", UniversalAnalyticsController, :import
|
post "/:website/settings/google-import", GoogleAnalyticsController, :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
|
|
||||||
|
|
||||||
delete "/:website/settings/forget-imported", SiteController, :forget_imported
|
delete "/:website/settings/forget-imported", SiteController, :forget_imported
|
||||||
delete "/:website/settings/forget-import/:import_id", SiteController, :forget_import
|
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>
|
<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, :access_token, value: @access_token) %>
|
||||||
|
|
@ -9,15 +9,28 @@
|
||||||
<%= case @start_date do %>
|
<%= case @start_date do %>
|
||||||
<% {:ok, start_date} -> %>
|
<% {:ok, start_date} -> %>
|
||||||
<div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
|
<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>
|
||||||
|
|
||||||
<div class="mt-6">
|
<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">
|
<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>
|
</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>
|
||||||
<div class="flex justify-between mt-3">
|
<div class="flex justify-between mt-3">
|
||||||
<div class="w-36">
|
<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 ->",
|
<%= link("Continue ->",
|
||||||
to:
|
to:
|
||||||
Routes.universal_analytics_path(@conn, :confirm, @site.domain,
|
Routes.google_analytics_path(@conn, :confirm, @site.domain,
|
||||||
view_id: @view_id,
|
property_or_view: @property_or_view,
|
||||||
access_token: @access_token,
|
access_token: @access_token,
|
||||||
refresh_token: @refresh_token,
|
refresh_token: @refresh_token,
|
||||||
expires_at: @expires_at,
|
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"
|
theme="bright"
|
||||||
href={Plausible.Google.API.import_authorize_url(@site.id, "import", legacy: false)}
|
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" />
|
<img src="/images/icon/google_analytics_logo.svg" alt="Google 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"
|
|
||||||
/>
|
|
||||||
</PlausibleWeb.Components.Generic.button_link>
|
</PlausibleWeb.Components.Generic.button_link>
|
||||||
|
|
||||||
<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
|
|
||||||
|
|
@ -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,
|
assert {:ok,
|
||||||
%{
|
[
|
||||||
"one.test" => [{"57238190 - one.test", "57238190"}],
|
{"one.test", [{"57238190 - one.test", "57238190"}]},
|
||||||
"two.test" => [{"54460083 - two.test", "54460083"}]
|
{"two.test", [{"54460083 - two.test", "54460083"}]}
|
||||||
}} == Google.UA.API.list_views("access_token")
|
]} == Google.UA.API.list_views("access_token")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "list_views/1 returns authentication_failed when request fails with HTTP 403" do
|
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
|
describe "get_property/2" do
|
||||||
test "returns tuple consisting of display name and value of a property" 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 ->
|
expect(Plausible.HTTPClient.Mock, :get, fn _url, _opts ->
|
||||||
{:ok, %Finch.Response{status: 200, body: result}}
|
{:ok, %Finch.Response{status: 200, body: result}}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert {:ok, {"GA4 - Flood-It! (properties/153293282)", "properties/153293282"}} =
|
assert {:ok,
|
||||||
GA4.API.get_property("some_access_token", "properties/153293282")
|
%{name: "account.one - GA4 (properties/428685444)", id: "properties/428685444"}} =
|
||||||
|
GA4.API.get_property("some_access_token", "properties/428685444")
|
||||||
end
|
end
|
||||||
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
|
||||||
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
|
describe "DELETE /:website/settings/:forget_import/:import_id" do
|
||||||
setup [:create_user, :log_in, :create_new_site, :add_imported_data]
|
setup [:create_user, :log_in, :create_new_site, :add_imported_data]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue