Make final touches to Imports & Exports (#4025)

* Make final touches to Imports & Exports

* Change import content copy depending on CSV imports and exports flag state

* Remove unused aliases
This commit is contained in:
Adrian Gruntkowski 2024-04-19 11:40:13 +02:00 committed by GitHub
parent c10580777e
commit fede2f0a8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 428 additions and 689 deletions

View File

@ -16,16 +16,14 @@ defmodule Plausible.Google.API do
@verified_permission_levels ["siteOwner", "siteFullUser", "siteRestrictedUser"] @verified_permission_levels ["siteOwner", "siteFullUser", "siteRestrictedUser"]
def search_console_authorize_url(site_id, redirect_to) do def search_console_authorize_url(site_id) do
"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=#{@search_console_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=#{@search_console_scope}&state=" <>
Jason.encode!([site_id, redirect_to]) Jason.encode!([site_id, "search-console"])
end end
def import_authorize_url(site_id, redirect_to, opts \\ []) do def import_authorize_url(site_id) do
legacy = Keyword.get(opts, :legacy, true)
"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]) Jason.encode!([site_id, "import"])
end end
def fetch_access_token!(code) do def fetch_access_token!(code) do

View File

@ -43,8 +43,14 @@ defmodule Plausible.Imported do
@spec tables() :: [String.t()] @spec tables() :: [String.t()]
def tables, do: @table_names def tables, do: @table_names
@spec max_complete_imports() :: non_neg_integer() @spec max_complete_imports(Site.t()) :: non_neg_integer()
def max_complete_imports(), do: @max_complete_imports def max_complete_imports(site) do
if FunWithFlags.enabled?(:imports_exports, for: site) do
@max_complete_imports
else
1
end
end
@spec load_import_data(Site.t()) :: Site.t() @spec load_import_data(Site.t()) :: Site.t()
def load_import_data(%{import_data_loaded: true} = site), do: site def load_import_data(%{import_data_loaded: true} = site), do: site

View File

@ -7,10 +7,5 @@ defmodule PlausibleWeb.Components.Settings do
import PlausibleWeb.Components.Generic import PlausibleWeb.Components.Generic
alias Plausible.Imported.SiteImport
require Plausible.Imported.SiteImport
embed_templates("../templates/site/settings_search_console.html") embed_templates("../templates/site/settings_search_console.html")
embed_templates("../templates/site/settings_google_import.html")
end end

View File

@ -697,25 +697,15 @@ defmodule PlausibleWeb.AuthController do
end end
def google_auth_callback(conn, %{"error" => error, "state" => state} = params) do def google_auth_callback(conn, %{"error" => error, "state" => state} = params) do
[site_id, _redirected_to, legacy, _ga4] = [site_id, redirected_to | _] = Jason.decode!(state)
case Jason.decode!(state) do
[site_id, redirect_to] ->
[site_id, redirect_to, true, false]
[site_id, redirect_to, legacy] ->
[site_id, redirect_to, legacy, false]
[site_id, redirect_to, legacy, ga4] ->
[site_id, redirect_to, legacy, ga4]
end
site = Repo.get(Plausible.Site, site_id) site = Repo.get(Plausible.Site, site_id)
redirect_route = redirect_route =
if legacy do if redirected_to == "import" do
Routes.site_path(conn, :settings_integrations, site.domain)
else
Routes.site_path(conn, :settings_imports_exports, site.domain) Routes.site_path(conn, :settings_imports_exports, site.domain)
else
Routes.site_path(conn, :settings_integrations, site.domain)
end end
case error do case error do
@ -750,14 +740,7 @@ 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] = [site_id, redirect_to | _] = Jason.decode!(state)
case Jason.decode!(state) do
[site_id, redirect_to] ->
[site_id, redirect_to, true]
[site_id, redirect_to, legacy] ->
[site_id, redirect_to, legacy]
end
site = Repo.get(Plausible.Site, site_id) site = Repo.get(Plausible.Site, site_id)
expires_at = NaiveDateTime.add(NaiveDateTime.utc_now(), res["expires_in"]) expires_at = NaiveDateTime.add(NaiveDateTime.utc_now(), res["expires_in"])
@ -769,8 +752,7 @@ defmodule PlausibleWeb.AuthController do
Routes.google_analytics_path(conn, :property_or_view_form, site.domain, Routes.google_analytics_path(conn, :property_or_view_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
) )
) )

View File

@ -16,8 +16,7 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
"refresh_token" => refresh_token, "refresh_token" => refresh_token,
"expires_at" => expires_at, "expires_at" => expires_at,
"start_date" => start_date, "start_date" => start_date,
"end_date" => end_date, "end_date" => end_date
"legacy" => legacy
}) do }) do
site = conn.assigns.site site = conn.assigns.site
@ -31,7 +30,6 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
expires_at: expires_at, expires_at: expires_at,
start_date: start_date, start_date: start_date,
end_date: end_date, end_date: end_date,
legacy: legacy,
layout: {PlausibleWeb.LayoutView, "focus.html"} layout: {PlausibleWeb.LayoutView, "focus.html"}
) )
end end
@ -41,24 +39,18 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
%{ %{
"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
} = params } = params
) do ) do
site = conn.assigns.site site = conn.assigns.site
redirect_route = redirect_route = Routes.site_path(conn, :settings_imports_exports, site.domain)
if legacy == "true" do
Routes.site_path(conn, :settings_integrations, site.domain)
else
Routes.site_path(conn, :settings_imports_exports, site.domain)
end
result = result =
if legacy == "true" do if FunWithFlags.enabled?(:imports_exports, for: site) do
Google.UA.API.list_views(access_token)
else
Google.API.list_properties_and_views(access_token) Google.API.list_properties_and_views(access_token)
else
Google.UA.API.list_views(access_token)
end end
error = error =
@ -84,7 +76,6 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
site: conn.assigns.site, site: conn.assigns.site,
properties_and_views: properties_and_views, properties_and_views: properties_and_views,
selected_property_or_view_error: error, selected_property_or_view_error: error,
legacy: legacy,
layout: {PlausibleWeb.LayoutView, "focus.html"} layout: {PlausibleWeb.LayoutView, "focus.html"}
) )
@ -123,18 +114,12 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
"property_or_view" => property_or_view, "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
} = params } = params
) do ) do
site = conn.assigns.site site = conn.assigns.site
redirect_route = redirect_route = Routes.site_path(conn, :settings_imports_exports, site.domain)
if legacy == "true" do
Routes.site_path(conn, :settings_integrations, site.domain)
else
Routes.site_path(conn, :settings_imports_exports, site.domain)
end
with {:ok, api_start_date} <- with {:ok, api_start_date} <-
Google.API.get_analytics_start_date(access_token, property_or_view), Google.API.get_analytics_start_date(access_token, property_or_view),
@ -156,15 +141,14 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
refresh_token: refresh_token, refresh_token: refresh_token,
expires_at: expires_at, expires_at: expires_at,
start_date: Date.to_iso8601(start_date), start_date: Date.to_iso8601(start_date),
end_date: Date.to_iso8601(end_date), end_date: Date.to_iso8601(end_date)
legacy: legacy
) )
) )
else else
{:error, error} when error in [:no_data, :no_time_window] -> {:error, error} when error in [:no_data, :no_time_window] ->
params = params =
params params
|> Map.take(["access_token", "refresh_token", "expires_at", "legacy"]) |> Map.take(["access_token", "refresh_token", "expires_at"])
|> Map.put("error", Atom.to_string(error)) |> Map.put("error", Atom.to_string(error))
property_or_view_form(conn, params) property_or_view_form(conn, params)
@ -201,20 +185,14 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
"refresh_token" => refresh_token, "refresh_token" => refresh_token,
"expires_at" => expires_at, "expires_at" => expires_at,
"start_date" => start_date, "start_date" => start_date,
"end_date" => end_date, "end_date" => end_date
"legacy" => legacy
}) do }) do
site = conn.assigns.site site = conn.assigns.site
start_date = Date.from_iso8601!(start_date) start_date = Date.from_iso8601!(start_date)
end_date = Date.from_iso8601!(end_date) end_date = Date.from_iso8601!(end_date)
redirect_route = redirect_route = Routes.site_path(conn, :settings_imports_exports, site.domain)
if legacy == "true" do
Routes.site_path(conn, :settings_integrations, site.domain)
else
Routes.site_path(conn, :settings_imports_exports, site.domain)
end
case Google.API.get_property_or_view(access_token, property_or_view) do case Google.API.get_property_or_view(access_token, property_or_view) do
{:ok, %{name: property_or_view_name, id: property_or_view}} -> {:ok, %{name: property_or_view_name, id: property_or_view}} ->
@ -230,7 +208,6 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
start_date: start_date, start_date: start_date,
end_date: end_date, end_date: end_date,
property?: Google.API.property?(property_or_view), property?: Google.API.property?(property_or_view),
legacy: legacy,
layout: {PlausibleWeb.LayoutView, "focus.html"} layout: {PlausibleWeb.LayoutView, "focus.html"}
) )
@ -274,8 +251,7 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
"end_date" => end_date, "end_date" => end_date,
"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
}) do }) do
site = conn.assigns.site site = conn.assigns.site
current_user = conn.assigns.current_user current_user = conn.assigns.current_user
@ -283,12 +259,7 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
start_date = Date.from_iso8601!(start_date) start_date = Date.from_iso8601!(start_date)
end_date = Date.from_iso8601!(end_date) end_date = Date.from_iso8601!(end_date)
redirect_route = redirect_route = Routes.site_path(conn, :settings_imports_exports, site.domain)
if legacy == "true" do
Routes.site_path(conn, :settings_integrations, site.domain)
else
Routes.site_path(conn, :settings_imports_exports, site.domain)
end
import_opts = [ import_opts = [
label: property_or_view, label: property_or_view,
@ -296,8 +267,7 @@ defmodule PlausibleWeb.GoogleAnalyticsController do
end_date: end_date, end_date: end_date,
access_token: access_token, access_token: access_token,
refresh_token: refresh_token, refresh_token: refresh_token,
token_expires_at: expires_at, token_expires_at: expires_at
legacy: legacy == "true"
] ]
with {:ok, start_date, end_date} <- Imported.clamp_dates(site, start_date, end_date), with {:ok, start_date, end_date} <- Imported.clamp_dates(site, start_date, end_date),

View File

@ -237,22 +237,11 @@ defmodule PlausibleWeb.SiteController do
Plausible.Google.API.fetch_verified_properties(site.google_auth) Plausible.Google.API.fetch_verified_properties(site.google_auth)
end end
legacy_import = Plausible.Imported.get_legacy_import(site)
imported_pageviews =
if legacy_import do
Plausible.Stats.Clickhouse.imported_pageview_count(site)
else
0
end
has_plugins_tokens? = Plausible.Plugins.API.Tokens.any?(site) has_plugins_tokens? = Plausible.Plugins.API.Tokens.any?(site)
conn conn
|> render("settings_integrations.html", |> render("settings_integrations.html",
site: site, site: site,
legacy_import: legacy_import,
imported_pageviews: imported_pageviews,
has_plugins_tokens?: has_plugins_tokens?, has_plugins_tokens?: has_plugins_tokens?,
search_console_domains: search_console_domains, search_console_domains: search_console_domains,
dogfood_page_path: "/:dashboard/settings/integrations", dogfood_page_path: "/:dashboard/settings/integrations",
@ -278,18 +267,13 @@ defmodule PlausibleWeb.SiteController do
def settings_imports_exports(conn, _params) do def settings_imports_exports(conn, _params) do
site = conn.assigns.site site = conn.assigns.site
if FunWithFlags.enabled?(:imports_exports, for: site) do conn
conn |> render("settings_imports_exports.html",
|> render("settings_imports_exports.html", site: site,
site: site, dogfood_page_path: "/:dashboard/settings/imports-exports",
dogfood_page_path: "/:dashboard/settings/imports-exports", connect_live_socket: true,
connect_live_socket: true, layout: {PlausibleWeb.LayoutView, "site_settings.html"}
layout: {PlausibleWeb.LayoutView, "site_settings.html"} )
)
else
conn
|> redirect(external: Routes.site_path(conn, :settings, site.domain))
end
end end
def update_google_auth(conn, %{"google_auth" => attrs}) do def update_google_auth(conn, %{"google_auth" => attrs}) do

View File

@ -35,10 +35,13 @@ defmodule PlausibleWeb.Live.ImportsExportsSettings do
|> assign_new(:current_user, fn -> |> assign_new(:current_user, fn ->
Plausible.Repo.get(Plausible.Auth.User, user_id) Plausible.Repo.get(Plausible.Auth.User, user_id)
end) end)
|> assign_new(:max_imports, fn %{site: site} ->
Imported.max_complete_imports(site)
end)
:ok = Imported.listen() :ok = Imported.listen()
{:ok, assign(socket, max_imports: Imported.max_complete_imports())} {:ok, socket}
end end
def render(assigns) do def render(assigns) do
@ -79,7 +82,7 @@ defmodule PlausibleWeb.Live.ImportsExportsSettings do
class="w-36 h-20" class="w-36 h-20"
theme="bright" theme="bright"
disabled={@import_in_progress? or @at_maximum?} disabled={@import_in_progress? or @at_maximum?}
href={Plausible.Google.API.import_authorize_url(@site.id, "import", legacy: false)} href={Plausible.Google.API.import_authorize_url(@site.id)}
> >
<img src="/images/icon/google_analytics_logo.svg" alt="Google Analytics import" /> <img src="/images/icon/google_analytics_logo.svg" alt="Google Analytics import" />
</.button_link> </.button_link>

View File

@ -4,7 +4,6 @@
<%= hidden_input(f, :access_token, value: @access_token) %> <%= hidden_input(f, :access_token, value: @access_token) %>
<%= hidden_input(f, :refresh_token, value: @refresh_token) %> <%= hidden_input(f, :refresh_token, value: @refresh_token) %>
<%= hidden_input(f, :expires_at, value: @expires_at) %> <%= 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"> <div class="mt-6 text-sm text-gray-500 dark:text-gray-200">
Stats from this Stats from this
@ -56,8 +55,7 @@
property_or_view: @selected_property_or_view, property_or_view: @selected_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
) )
} }
class="underline text-indigo-600" class="underline text-indigo-600"

View File

@ -4,7 +4,6 @@
<%= hidden_input(f, :access_token, value: @access_token) %> <%= hidden_input(f, :access_token, value: @access_token) %>
<%= hidden_input(f, :refresh_token, value: @refresh_token) %> <%= hidden_input(f, :refresh_token, value: @refresh_token) %>
<%= hidden_input(f, :expires_at, value: @expires_at) %> <%= 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"> <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. Choose the property or view in your Google Analytics account that will be imported to the <%= @site.domain %> dashboard.

View File

@ -40,8 +40,7 @@
property_or_view: @property_or_view, 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
) )
} }
class="underline text-indigo-600" class="underline text-indigo-600"
@ -58,8 +57,7 @@
refresh_token: @refresh_token, refresh_token: @refresh_token,
expires_at: @expires_at, expires_at: @expires_at,
start_date: @start_date, start_date: @start_date,
end_date: @end_date, end_date: @end_date
legacy: @legacy
), ),
class: "button sm:w-auto w-full" class: "button sm:w-auto w-full"
) %> ) %>

View File

@ -1,124 +0,0 @@
<div class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden py-6 px-4 sm:p-6">
<header class="relative border-b border-gray-200 pb-4">
<h2 class="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">
Google Analytics Data Import
</h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">
Import existing data from your Google Analytics account.
</p>
<PlausibleWeb.Components.Generic.docs_info slug="google-analytics-import" />
</header>
<%= if Keyword.get(Application.get_env(:plausible, :google), :client_id) do %>
<%= cond do %>
<% @legacy_import && @legacy_import.status in [SiteImport.pending(), SiteImport.importing()] -> %>
<li class="py-4 flex items-center justify-between space-x-4">
<div class="flex flex-col">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
Import from Google Analytics
<svg
class="animate-spin -mr-1 ml-1 h-4 w-4 inline text-indigo-600"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
>
</circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
>
</path>
</svg>
</p>
<p class="text-sm leading-5 text-gray-500 dark:text-gray-200">
From <%= PlausibleWeb.EmailView.date_format(@legacy_import.start_date) %> to <%= PlausibleWeb.EmailView.date_format(
@legacy_import.end_date
) %>
</p>
</div>
<%= link("Cancel import",
to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported",
method: :delete,
class:
"inline-block mt-4 px-4 py-2 border border-gray-300 dark:border-gray-500 text-sm leading-5 font-medium rounded-md text-red-700 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:border-blue-300 focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150"
) %>
</li>
<% @legacy_import && @legacy_import.status == SiteImport.completed() -> %>
<li class="py-4 flex items-center justify-between space-x-4">
<div class="flex flex-col">
<p class="text-sm leading-5 font-medium text-gray-900 dark:text-gray-100">
Import from Google Analytics
<svg
class="h-4 w-4 inline ml-1 -mt-1 text-green-600"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
</p>
<p class="text-sm leading-5 text-gray-500 dark:text-gray-200">
From <%= PlausibleWeb.EmailView.date_format(@legacy_import.start_date) %> to <%= PlausibleWeb.EmailView.date_format(
@legacy_import.end_date
) %>
</p>
</div>
<%= link(
"Clear " <>
PlausibleWeb.StatsView.large_number_format(@imported_pageviews) <>
" Imported Pageviews",
to: "/#{URI.encode_www_form(@site.domain)}/settings/forget-imported",
method: :delete,
class:
"inline-block mt-4 px-4 py-2 text-sm leading-5 font-medium text-red-600 bg-white dark:bg-gray-800 hover:text-red-500 dark:hover:text-red-400 focus:outline-none focus:ring active:text-red-800 active:bg-gray-50 transition ease-in-out duration-150"
) %>
</li>
<% true -> %>
<%= if @legacy_import && @legacy_import.status == SiteImport.failed() do %>
<div class="text-sm mt-2 text-gray-900 dark:text-gray-100">
Your latest import has failed. You can try importing again by clicking the button below. If you try multiple times and the import keeps failing, please contact support.
</div>
<% end %>
<PlausibleWeb.Components.Google.button
id="analytics-connect"
to={Plausible.Google.API.import_authorize_url(@site.id, "import", legacy: true)}
/>
<% end %>
<% else %>
<div class="my-8 text-center text-lg">
<svg
class="block mx-auto mb-4 w-6 h-6 text-yellow-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
>
</path>
</svg>
<p class="text-gray-900 dark:text-gray-200">
An extra step is needed to set up your Plausible Analytics Self Hosted for the Google Search Console integration.
Find instructions <%= link("here",
to: "https://plausible.io/docs/self-hosting-configuration#google-search-integration",
class: "text-indigo-500"
) %>
</p>
</div>
<% end %>
</div>

View File

@ -4,7 +4,12 @@
Import Data Import Data
</h2> </h2>
<p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200"> <p class="mt-1 text-sm leading-5 text-gray-500 dark:text-gray-200">
Import existing data from external sources. Pick one of the options below to start a new import. Import existing data from external sources.
<%= if FunWithFlags.enabled?(:csv_imports_exports, for: @site) do %>
Pick one of the options below to start a new import.
<% else %>
Click below to start a new import.
<% end %>
</p> </p>
<PlausibleWeb.Components.Generic.docs_info slug="google-analytics-import" /> <PlausibleWeb.Components.Generic.docs_info slug="google-analytics-import" />

View File

@ -3,14 +3,6 @@
search_console_domains={@search_console_domains} search_console_domains={@search_console_domains}
/> />
<%= if not FunWithFlags.enabled?(:imports_exports, for: @site) do %>
<PlausibleWeb.Components.Settings.settings_google_import
site={@site}
imported_pageviews={@imported_pageviews}
legacy_import={@legacy_import}
/>
<% end %>
<section <section
:if={@has_plugins_tokens? || @conn.query_params["new_token"]} :if={@has_plugins_tokens? || @conn.query_params["new_token"]}
class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden" class="shadow bg-white dark:bg-gray-800 sm:rounded-md sm:overflow-hidden"

View File

@ -82,7 +82,7 @@
<% else %> <% else %>
<PlausibleWeb.Components.Google.button <PlausibleWeb.Components.Google.button
id="search-console-connect" id="search-console-connect"
to={Plausible.Google.API.search_console_authorize_url(@site.id, "search-console")} to={Plausible.Google.API.search_console_authorize_url(@site.id)}
/> />
<div class="text-gray-700 dark:text-gray-300 mt-8"> <div class="text-gray-700 dark:text-gray-300 mt-8">
NB: You also need to set up your site on NB: You also need to set up your site on

View File

@ -49,6 +49,13 @@ defmodule PlausibleWeb.LayoutView do
end end
def settings_tabs(conn) do def settings_tabs(conn) do
imports_exports_key =
if FunWithFlags.enabled?(:csv_imports_exports, for: conn.assigns.site) do
"Imports & Exports"
else
"Imports"
end
[ [
%{key: "General", value: "general", icon: :rocket_launch}, %{key: "General", value: "general", icon: :rocket_launch},
%{key: "People", value: "people", icon: :users}, %{key: "People", value: "people", icon: :users},
@ -59,9 +66,7 @@ defmodule PlausibleWeb.LayoutView do
end, end,
%{key: "Custom Properties", value: "properties", icon: :document_text}, %{key: "Custom Properties", value: "properties", icon: :document_text},
%{key: "Integrations", value: "integrations", icon: :arrow_path_rounded_square}, %{key: "Integrations", value: "integrations", icon: :arrow_path_rounded_square},
if FunWithFlags.enabled?(:imports_exports, for: conn.assigns.site) do %{key: imports_exports_key, value: "imports-exports", icon: :arrows_up_down},
%{key: "Imports & Exports", value: "imports-exports", icon: :arrows_up_down}
end,
%{ %{
key: "Shields", key: "Shields",
icon: :shield_exclamation, icon: :shield_exclamation,

View File

@ -2,10 +2,6 @@ defmodule PlausibleWeb.SiteView do
use PlausibleWeb, :view use PlausibleWeb, :view
use Plausible use Plausible
alias Plausible.Imported.SiteImport
require Plausible.Imported.SiteImport
def plausible_url do def plausible_url do
PlausibleWeb.Endpoint.url() PlausibleWeb.Endpoint.url()
end end

View File

@ -1484,7 +1484,7 @@ defmodule PlausibleWeb.AuthControllerTest do
conn = get(conn, Routes.auth_path(conn, :google_auth_callback), callback_params) conn = get(conn, Routes.auth_path(conn, :google_auth_callback), callback_params)
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
Routes.site_path(conn, :settings_integrations, site.domain) Routes.site_path(conn, :settings_imports_exports, site.domain)
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"unable to authenticate your Google Analytics" "unable to authenticate your Google Analytics"

View File

@ -24,8 +24,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2020-02-22", "start_date" => "2020-02-22",
"end_date" => "2022-09-22", "end_date" => "2022-09-22"
"legacy" => "true"
}) })
|> html_response(200) |> html_response(200)
@ -36,8 +35,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
refresh_token: "foo", refresh_token: "foo",
expires_at: "2022-09-22T20:01:37.112777", expires_at: "2022-09-22T20:01:37.112777",
start_date: "2020-02-22", start_date: "2020-02-22",
end_date: "2022-09-22", end_date: "2022-09-22"
legacy: "true"
) )
|> String.replace("&", "&amp;") |> String.replace("&", "&amp;")
end end
@ -46,30 +44,6 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
describe "GET /:website/import/google-analytics/property-or-view" do describe "GET /:website/import/google-analytics/property-or-view" do
setup [:create_user, :log_in, :create_new_site] 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 test "lists Google Analytics views and properties", %{conn: conn, site: site} do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
@ -94,8 +68,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{ |> get("/#{site.domain}/import/google-analytics/property-or-view", %{
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
|> html_response(200) |> html_response(200)
@ -106,176 +79,166 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert response =~ "GA4 - Google Merch Shop (properties/213025502)" assert response =~ "GA4 - Google Merch Shop (properties/213025502)"
end end
for {legacy, view} <- [{"true", :settings_integrations}, {"false", :settings_imports_exports}] do test "redirects to imports and exports on auth error with flash error", %{
test "redirects to #{view} on auth error with flash error (legacy: #{legacy})", %{ conn: conn,
conn: conn, site: site
site: site } do
} do expect(
expect( Plausible.HTTPClient.Mock,
Plausible.HTTPClient.Mock, :get,
:get, fn _url, _opts ->
fn _url, _opts -> {:error, %HTTPClient.Non200Error{reason: %{status: 403, body: %{}}}}
{:error, %HTTPClient.Non200Error{reason: %{status: 403, body: %{}}}} end
end )
)
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{ |> get("/#{site.domain}/import/google-analytics/property-or-view", %{
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"We were unable to authenticate your Google Analytics account" "We were unable to authenticate your Google Analytics account"
end end
test "redirects to #{view} on timeout error with flash error (legacy: #{legacy})", %{ test "redirects to imports and exports on timeout error with flash error", %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:get, :get,
fn _url, _opts -> fn _url, _opts ->
{:error, %Mint.TransportError{reason: :timeout}} {:error, %Mint.TransportError{reason: :timeout}}
end end
) )
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{ |> get("/#{site.domain}/import/google-analytics/property-or-view", %{
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Google Analytics API has timed out." "Google Analytics API has timed out."
end end
test "redirects to #{view} on list retrival failure with flash error (legacy: #{legacy})", test "redirects to imports and exports on list retrival failure with flash error",
%{ %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:get, :get,
fn _url, _opts -> fn _url, _opts ->
{:error, {:error, %HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}}
%HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}} end
end )
)
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/property-or-view", %{ |> get("/#{site.domain}/import/google-analytics/property-or-view", %{
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"We were unable to list your Google Analytics properties and views" "We were unable to list your Google Analytics properties and views"
end
end end
end end
describe "POST /:website/import/google-analytics/property-or-view" do describe "POST /:website/import/google-analytics/property-or-view" do
setup [:create_user, :log_in, :create_new_site] setup [:create_user, :log_in, :create_new_site]
for legacy <- ["true", "false"] do test "redirects to user metrics notice", %{conn: conn, site: site} do
test "redirects to user metrics notice (UA legacy: #{legacy})", %{conn: conn, site: site} do expect(
expect( Plausible.HTTPClient.Mock,
Plausible.HTTPClient.Mock, :post,
:post, fn _url, _opts, _params ->
fn _url, _opts, _params -> body = "fixture/ga_start_date.json" |> File.read!() |> Jason.decode!()
body = "fixture/ga_start_date.json" |> File.read!() |> Jason.decode!() {:ok, %Finch.Response{body: body, status: 200}}
{:ok, %Finch.Response{body: body, status: 200}} end
end )
)
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:post, :post,
fn _url, _opts, _params -> fn _url, _opts, _params ->
body = "fixture/ga_end_date.json" |> File.read!() |> Jason.decode!() body = "fixture/ga_end_date.json" |> File.read!() |> Jason.decode!()
{:ok, %Finch.Response{body: body, status: 200}} {:ok, %Finch.Response{body: body, status: 200}}
end end
) )
conn = conn =
conn conn
|> post("/#{site.domain}/import/google-analytics/property-or-view", %{ |> post("/#{site.domain}/import/google-analytics/property-or-view", %{
"property_or_view" => "57238190", "property_or_view" => "57238190",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) =~ assert redirected_to(conn, 302) =~
"/#{URI.encode_www_form(site.domain)}/import/google-analytics/user-metric" "/#{URI.encode_www_form(site.domain)}/import/google-analytics/user-metric"
end end
test "redirects to confirmation (UA legacy: #{legacy})", %{conn: conn, site: site} do test "redirects to confirmation", %{conn: conn, site: site} do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:post, :post,
fn _url, _opts, _params -> fn _url, _opts, _params ->
body = "fixture/ga_start_date_later.json" |> File.read!() |> Jason.decode!() body = "fixture/ga_start_date_later.json" |> File.read!() |> Jason.decode!()
{:ok, %Finch.Response{body: body, status: 200}} {:ok, %Finch.Response{body: body, status: 200}}
end end
) )
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:post, :post,
fn _url, _opts, _params -> fn _url, _opts, _params ->
body = "fixture/ga_end_date.json" |> File.read!() |> Jason.decode!() body = "fixture/ga_end_date.json" |> File.read!() |> Jason.decode!()
{:ok, %Finch.Response{body: body, status: 200}} {:ok, %Finch.Response{body: body, status: 200}}
end end
) )
conn = conn =
conn conn
|> post("/#{site.domain}/import/google-analytics/property-or-view", %{ |> post("/#{site.domain}/import/google-analytics/property-or-view", %{
"property_or_view" => "57238190", "property_or_view" => "57238190",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) =~ assert redirected_to(conn, 302) =~
"/#{URI.encode_www_form(site.domain)}/import/google-analytics/confirm" "/#{URI.encode_www_form(site.domain)}/import/google-analytics/confirm"
end
end end
test "redirects to confirmation (GA4)", %{conn: conn, site: site} do test "redirects to confirmation (GA4)", %{conn: conn, site: site} do
@ -303,8 +266,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
assert redirected_to(conn, 302) =~ assert redirected_to(conn, 302) =~
@ -365,8 +327,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
|> html_response(200) |> html_response(200)
@ -415,117 +376,110 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
|> html_response(200) |> html_response(200)
assert response =~ "No data found. Nothing to import." assert response =~ "No data found. Nothing to import."
end end
for {legacy, view} <- [{"true", :settings_integrations}, {"false", :settings_imports_exports}] do test "redirects to imports and exports on failed property/view choice with flash error",
test "redirects to #{view} on failed property/view choice with flash error (legacy: #{legacy})", %{
%{ conn: conn,
conn: conn, site: site
site: site } do
} do expect(
expect( Plausible.HTTPClient.Mock,
Plausible.HTTPClient.Mock, :post,
:post, fn _url, _opts, _params ->
fn _url, _opts, _params -> {:error, %HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}}
{:error, end
%HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}} )
end
)
conn = conn =
conn conn
|> post("/#{site.domain}/import/google-analytics/property-or-view", %{ |> post("/#{site.domain}/import/google-analytics/property-or-view", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"We were unable to retrieve information from Google Analytics" "We were unable to retrieve information from Google Analytics"
end end
test "redirects to #{view} on expired authentication with flash error (legacy: #{legacy})", test "redirects to imports and exports on expired authentication with flash error",
%{ %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:post, :post,
fn _url, _opts, _params -> fn _url, _opts, _params ->
{:error, %HTTPClient.Non200Error{reason: %{status: 403, body: "Access denied"}}} {:error, %HTTPClient.Non200Error{reason: %{status: 403, body: "Access denied"}}}
end end
) )
conn = conn =
conn conn
|> post("/#{site.domain}/import/google-analytics/property-or-view", %{ |> post("/#{site.domain}/import/google-analytics/property-or-view", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Google Analytics authentication seems to have expired." "Google Analytics authentication seems to have expired."
end end
test "redirects to #{view} on timeout with flash error (legacy: #{legacy})", test "redirects to imports and exports on timeout with flash error",
%{ %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:post, :post,
fn _url, _opts, _params -> fn _url, _opts, _params ->
{:error, %Mint.TransportError{reason: :timeout}} {:error, %Mint.TransportError{reason: :timeout}}
end end
) )
conn = conn =
conn conn
|> post("/#{site.domain}/import/google-analytics/property-or-view", %{ |> post("/#{site.domain}/import/google-analytics/property-or-view", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Google Analytics API has timed out." "Google Analytics API has timed out."
end
end end
end end
@ -550,8 +504,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2012-01-18", "start_date" => "2012-01-18",
"end_date" => "2022-09-22", "end_date" => "2022-09-22"
"legacy" => "true"
}) })
|> html_response(200) |> html_response(200)
@ -565,8 +518,6 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert text_of_attr(response, ~s|input[name=expires_at]|, "value") == assert text_of_attr(response, ~s|input[name=expires_at]|, "value") ==
"2022-09-22T20:01:37.112777" "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=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=start_date]|, "value") == "2012-01-18"
@ -592,8 +543,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2024-02-22", "start_date" => "2024-02-22",
"end_date" => "2024-02-26", "end_date" => "2024-02-26"
"legacy" => "true"
}) })
|> html_response(200) |> html_response(200)
@ -607,8 +557,6 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert text_of_attr(response, ~s|input[name=expires_at]|, "value") == assert text_of_attr(response, ~s|input[name=expires_at]|, "value") ==
"2022-09-22T20:01:37.112777" "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") == assert text_of_attr(response, ~s|input[name=property_or_view]|, "value") ==
"properties/428685444" "properties/428685444"
@ -617,115 +565,109 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert text_of_attr(response, ~s|input[name=end_date]|, "value") == "2024-02-26" assert text_of_attr(response, ~s|input[name=end_date]|, "value") == "2024-02-26"
end end
for {legacy, view} <- [{"true", :settings_integrations}, {"false", :settings_imports_exports}] do test "redirects to imports and exports on failed property/view retrieval with flash error",
test "redirects to #{view} on failed property/view retrieval with flash error (legacy: #{legacy})", %{
%{ conn: conn,
conn: conn, site: site
site: site } do
} do expect(
expect( Plausible.HTTPClient.Mock,
Plausible.HTTPClient.Mock, :get,
:get, fn _url, _params ->
fn _url, _params -> {:error, %HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}}
{:error, end
%HTTPClient.Non200Error{reason: %{status: 500, body: "Internal server error"}}} )
end
)
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/confirm", %{ |> get("/#{site.domain}/import/google-analytics/confirm", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2024-02-22", "start_date" => "2024-02-22",
"end_date" => "2024-02-26", "end_date" => "2024-02-26"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"We were unable to retrieve information from Google Analytics" "We were unable to retrieve information from Google Analytics"
end end
test "redirects to #{view} on expired authentication with flash error (legacy: #{legacy})", test "redirects to imports and exports on expired authentication with flash error",
%{ %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:get, :get,
fn _url, _params -> fn _url, _params ->
{:error, %HTTPClient.Non200Error{reason: %{status: 403, body: "Access denied"}}} {:error, %HTTPClient.Non200Error{reason: %{status: 403, body: "Access denied"}}}
end end
) )
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/confirm", %{ |> get("/#{site.domain}/import/google-analytics/confirm", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2024-02-22", "start_date" => "2024-02-22",
"end_date" => "2024-02-26", "end_date" => "2024-02-26"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Google Analytics authentication seems to have expired." "Google Analytics authentication seems to have expired."
end end
test "redirects to #{view} on timeout with flash error (legacy: #{legacy})", test "redirects to imports and exports on timeout with flash error",
%{ %{
conn: conn, conn: conn,
site: site site: site
} do } do
expect( expect(
Plausible.HTTPClient.Mock, Plausible.HTTPClient.Mock,
:get, :get,
fn _url, _params -> fn _url, _params ->
{:error, %Mint.TransportError{reason: :timeout}} {:error, %Mint.TransportError{reason: :timeout}}
end end
) )
conn = conn =
conn conn
|> get("/#{site.domain}/import/google-analytics/confirm", %{ |> get("/#{site.domain}/import/google-analytics/confirm", %{
"property_or_view" => "properties/428685906", "property_or_view" => "properties/428685906",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777",
"start_date" => "2024-02-22", "start_date" => "2024-02-22",
"end_date" => "2024-02-26", "end_date" => "2024-02-26"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Google Analytics API has timed out." "Google Analytics API has timed out."
end
end end
end end
@ -740,8 +682,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
@ -762,12 +703,11 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "true"
}) })
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path(conn, :settings_integrations, site.domain) PlausibleWeb.Router.Helpers.site_path(conn, :settings_imports_exports, site.domain)
[site_import] = Plausible.Imported.list_all_imports(site) [site_import] = Plausible.Imported.list_all_imports(site)
@ -776,7 +716,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert site_import.status == SiteImport.pending() assert site_import.status == SiteImport.pending()
end end
test "redirects to imports and exports when creating UA job with legacy set to false", %{ test "redirects to imports and exports when creating UA job", %{
conn: conn, conn: conn,
site: site site: site
} do } do
@ -787,8 +727,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
@ -808,8 +747,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
assert [%{id: import_id, legacy: false}] = Plausible.Imported.list_all_imports(site) assert [%{id: import_id, legacy: false}] = Plausible.Imported.list_all_imports(site)
@ -835,11 +773,10 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "true"
}) })
assert [%{id: import_id, legacy: true}] = Plausible.Imported.list_all_imports(site) assert [%{id: import_id, legacy: false}] = Plausible.Imported.list_all_imports(site)
assert_enqueued( assert_enqueued(
worker: Plausible.Workers.ImportAnalytics, worker: Plausible.Workers.ImportAnalytics,
@ -872,8 +809,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "false"
}) })
conn = conn =
@ -883,8 +819,7 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => "true"
}) })
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
@ -895,43 +830,40 @@ defmodule PlausibleWeb.GoogleAnalyticsControllerTest do
assert job.args.import_id == import_id assert job.args.import_id == import_id
end end
for {legacy, view} <- [{"true", :settings_integrations}, {"false", :settings_imports_exports}] do test "redirects to imports and exports with no time window error flash error", %{
test "redirects to #{view} with no time window error flash error (legacy: #{legacy})", %{ conn: conn,
conn: conn, site: site
site: site } do
} do start_date = ~D[2022-01-12]
start_date = ~D[2022-01-12] end_date = ~D[2024-03-13]
end_date = ~D[2024-03-13]
_existing_import = _existing_import =
insert(:site_import, insert(:site_import,
site: site, site: site,
start_date: start_date, start_date: start_date,
end_date: end_date, end_date: end_date,
status: :completed status: :completed
) )
conn = conn =
post(conn, "/#{site.domain}/settings/google-import", %{ post(conn, "/#{site.domain}/settings/google-import", %{
"property_or_view" => "123456", "property_or_view" => "123456",
"start_date" => "2023-03-01", "start_date" => "2023-03-01",
"end_date" => "2022-03-01", "end_date" => "2022-03-01",
"access_token" => "token", "access_token" => "token",
"refresh_token" => "foo", "refresh_token" => "foo",
"expires_at" => "2022-09-22T20:01:37.112777", "expires_at" => "2022-09-22T20:01:37.112777"
"legacy" => unquote(legacy) })
})
assert redirected_to(conn, 302) == assert redirected_to(conn, 302) ==
PlausibleWeb.Router.Helpers.site_path( PlausibleWeb.Router.Helpers.site_path(
conn, conn,
unquote(view), :settings_imports_exports,
site.domain site.domain
) )
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ assert Phoenix.Flash.get(conn.assigns.flash, :error) =~
"Import failed. No data could be imported because date range overlaps with existing data." "Import failed. No data could be imported because date range overlaps with existing data."
end
end end
end end
end end

View File

@ -679,7 +679,7 @@ defmodule PlausibleWeb.SiteControllerTest do
end end
test "disables import buttons when imports are at maximum", %{conn: conn, site: site} do test "disables import buttons when imports are at maximum", %{conn: conn, site: site} do
insert_list(Plausible.Imported.max_complete_imports(), :site_import, insert_list(Plausible.Imported.max_complete_imports(site), :site_import,
site: site, site: site,
status: SiteImport.completed() status: SiteImport.completed()
) )
@ -687,7 +687,7 @@ defmodule PlausibleWeb.SiteControllerTest do
conn = get(conn, "/#{site.domain}/settings/imports-exports") conn = get(conn, "/#{site.domain}/settings/imports-exports")
assert html_response(conn, 200) =~ assert html_response(conn, 200) =~
"Maximum of #{Plausible.Imported.max_complete_imports()} imports is reached." "Maximum of #{Plausible.Imported.max_complete_imports(site)} imports is reached."
end end
test "considers older legacy imports when showing pageview count", %{conn: conn, site: site} do test "considers older legacy imports when showing pageview count", %{conn: conn, site: site} do