From 594b0854671a245ed2247d9ccc5fbc3b79acff00 Mon Sep 17 00:00:00 2001 From: Uku Taht Date: Tue, 24 Mar 2020 10:50:16 +0200 Subject: [PATCH] Analytics without using cookies (#44) * Use fingerprints instead of user_id * Fix tests --- assets/js/plausible.js | 54 +++++-------- lib/plausible/stats/stats.ex | 58 +++++++------- .../controllers/api/external_controller.ex | 10 --- .../templates/layout/_tracking.html.eex | 4 +- .../20200317142459_backfill_fingerprints.exs | 14 ++++ .../ingest/fingerprint_session_test.exs | 80 +++++++++++++++++++ .../api/external_controller_test.exs | 60 +++++--------- .../current_visitors_test.exs | 2 +- .../api/stats_controller/main_graph_test.exs | 12 +-- .../api/stats_controller/referrers_test.exs | 2 +- test/support/factory.ex | 9 ++- 11 files changed, 175 insertions(+), 130 deletions(-) create mode 100644 priv/repo/migrations/20200317142459_backfill_fingerprints.exs create mode 100644 test/plausible/ingest/fingerprint_session_test.exs diff --git a/assets/js/plausible.js b/assets/js/plausible.js index 4fa173a26..ddc75a342 100644 --- a/assets/js/plausible.js +++ b/assets/js/plausible.js @@ -4,9 +4,11 @@ try { const scriptEl = window.document.querySelector('[src*="' + plausibleHost +'"]') const domainAttr = scriptEl && scriptEl.getAttribute('data-domain') + const trackAcquisitionAttr = scriptEl && scriptEl.getAttribute('data-track-acquisition') const CONFIG = { - domain: domainAttr || window.location.hostname + domain: domainAttr || window.location.hostname, + trackAcquisition: typeof(trackAcquisitionAttr) === 'string' } function setCookie(name,value) { @@ -23,18 +25,6 @@ return matches ? decodeURIComponent(matches[1]) : null; } - function pseudoUUIDv4() { - var d = new Date().getTime(); - if (typeof performance !== 'undefined' && typeof performance.now === 'function'){ - d += performance.now(); //use high-precision timer if available - } - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - var r = (d + Math.random() * 16) % 16 | 0; - d = Math.floor(d / 16); - return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); - }); - } - function ignore(reason) { console.warn('[Plausible] Ignoring event because ' + reason); } @@ -52,30 +42,23 @@ var userData = JSON.parse(getCookie('plausible_user')) if (userData) { - userData.new_visitor = false - if (userData.referrer) { - userData.initial_referrer = userData.referrer && decodeURIComponent(userData.referrer) - } else { - userData.initial_referrer = userData.initial_referrer && decodeURIComponent(userData.initial_referrer) - userData.initial_source = userData.initial_source && decodeURIComponent(userData.initial_source) - } - return userData - } else { return { - uid: pseudoUUIDv4(), - new_visitor: true, - initial_referrer: window.document.referrer, + initial_referrer: userData.initial_referrer && decodeURIComponent(userData.initial_referrer), + initial_source: userData.initial_source && decodeURIComponent(userData.initial_source) + } + } else { + userData = { + initial_referrer: window.document.referrer || null, initial_source: getSourceFromQueryParam(), } - } - } - function setUserData(payload) { - setCookie('plausible_user', JSON.stringify({ - uid: payload.uid, - initial_referrer: payload.initial_referrer && encodeURIComponent(payload.initial_referrer), - initial_source: payload.initial_source && encodeURIComponent(payload.initial_source), - })) + setCookie('plausible_user', JSON.stringify({ + initial_referrer: userData.initial_referrer && encodeURIComponent(userData.initial_referrer), + initial_source: userData.initial_source && encodeURIComponent(userData.initial_source), + })) + + return userData + } } function trigger(eventName, options) { @@ -83,11 +66,11 @@ if (window.location.protocol === 'file:') return ignore('website is running locally'); if (window.document.visibilityState === 'prerender') return ignore('document is prerendering'); - var payload = getUserData() + var payload = CONFIG['trackAcquisition'] ? getUserData() : {} payload.name = eventName payload.url = getUrl() payload.domain = CONFIG['domain'] - payload.referrer = window.document.referrer + payload.referrer = window.document.referrer || null payload.source = getSourceFromQueryParam() payload.user_agent = window.navigator.userAgent payload.screen_width = window.innerWidth @@ -100,7 +83,6 @@ request.onreadystatechange = function() { if (request.readyState == XMLHttpRequest.DONE) { - setUserData(payload) options && options.callback && options.callback() } } diff --git a/lib/plausible/stats/stats.ex b/lib/plausible/stats/stats.ex index e8f838978..91ecaf9fa 100644 --- a/lib/plausible/stats/stats.ex +++ b/lib/plausible/stats/stats.ex @@ -32,7 +32,7 @@ defmodule Plausible.Stats do from e in base_query(site, %{query | filters: %{}}), group_by: 1, order_by: 1, - select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end) @@ -41,7 +41,7 @@ defmodule Plausible.Stats do from e in base_query(site, query), group_by: 1, order_by: 1, - select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('month', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end) end @@ -61,7 +61,7 @@ defmodule Plausible.Stats do from e in base_query(site, %{ query | filters: %{} }), group_by: 1, order_by: 1, - select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end) @@ -70,7 +70,7 @@ defmodule Plausible.Stats do from e in base_query(site, query), group_by: 1, order_by: 1, - select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('day', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.to_date(dt) end) end @@ -98,7 +98,7 @@ defmodule Plausible.Stats do from e in base_query(site, %{query | filters: %{}}), group_by: 1, order_by: 1, - select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.truncate(dt, :second) end) @@ -108,7 +108,7 @@ defmodule Plausible.Stats do from e in base_query(site, query), group_by: 1, order_by: 1, - select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.user_id, :distinct)} + select: {fragment("date_trunc('hour', ? at time zone 'utc' at time zone ?)", e.timestamp, ^site.timezone), count(e.fingerprint, :distinct)} ) |> Enum.into(%{}) |> transform_keys(fn dt -> NaiveDateTime.truncate(dt, :second) end) @@ -125,7 +125,7 @@ defmodule Plausible.Stats do def bounce_rate(site, query) do {first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone) - sessions_query = from(s in Plausible.Session, + sessions_query = from(s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime ) @@ -141,20 +141,20 @@ defmodule Plausible.Stats do def pageviews_and_visitors(site, query) do Repo.one(from( e in base_query(site, query), - select: {count(e.id), count(e.user_id, :distinct)} + select: {count(e.id), count(e.fingerprint, :distinct)} )) end def unique_visitors(site, query) do Repo.one(from( e in base_query(site, query), - select: count(e.user_id, :distinct) + select: count(e.fingerprint, :distinct) )) end def top_referrers_for_goal(site, query, limit \\ 5) do Repo.all(from e in base_query(site, query), - select: %{name: e.initial_referrer_source, url: min(e.initial_referrer), count: count(e.user_id, :distinct)}, + select: %{name: e.initial_referrer_source, url: min(e.initial_referrer), count: count(e.fingerprint, :distinct)}, group_by: e.initial_referrer_source, where: not is_nil(e.initial_referrer_source), order_by: [desc: 3], @@ -166,7 +166,7 @@ defmodule Plausible.Stats do def top_referrers(site, query, limit \\ 5, include \\ []) do referrers = Repo.all(from e in base_query(site, query), - select: %{name: e.referrer_source, url: min(e.referrer), count: count(e.user_id, :distinct)}, + select: %{name: e.referrer_source, url: min(e.referrer), count: count(e.fingerprint, :distinct)}, group_by: e.referrer_source, where: not is_nil(e.referrer_source), order_by: [desc: 3], @@ -190,7 +190,7 @@ defmodule Plausible.Stats do {first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone) total_sessions_by_referrer = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.referrer_source in ^referrers, @@ -199,7 +199,7 @@ defmodule Plausible.Stats do ) |> Enum.into(%{}) bounced_sessions_by_referrer = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.is_bounce, @@ -223,7 +223,7 @@ defmodule Plausible.Stats do def visitors_from_referrer(site, query, referrer) do Repo.one( from e in base_query(site, query), - select: count(e.user_id, :distinct), + select: count(e.fingerprint, :distinct), where: e.referrer_source == ^referrer ) end @@ -231,7 +231,7 @@ defmodule Plausible.Stats do def conversions_from_referrer(site, query, referrer) do Repo.one( from e in base_query(site, query), - select: count(e.user_id, :distinct), + select: count(e.fingerprint, :distinct), where: e.initial_referrer_source == ^referrer ) end @@ -239,7 +239,7 @@ defmodule Plausible.Stats do def referrer_drilldown(site, query, referrer, include \\ []) do referring_urls = Repo.all( from e in base_query(site, query), - select: %{name: e.referrer, count: count(e.user_id, :distinct)}, + select: %{name: e.referrer, count: count(e.fingerprint, :distinct)}, group_by: e.referrer, where: e.referrer_source == ^referrer, order_by: [desc: 2], @@ -275,7 +275,7 @@ defmodule Plausible.Stats do def referrer_drilldown_for_goal(site, query, referrer) do Repo.all( from e in base_query(site, query), - select: %{name: e.initial_referrer, count: count(e.user_id, :distinct)}, + select: %{name: e.initial_referrer, count: count(e.fingerprint, :distinct)}, group_by: e.initial_referrer, where: e.initial_referrer_source == ^referrer, order_by: [desc: 2], @@ -287,7 +287,7 @@ defmodule Plausible.Stats do {first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone) total_sessions_by_url = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.referrer in ^referring_urls, @@ -296,7 +296,7 @@ defmodule Plausible.Stats do ) |> Enum.into(%{}) bounced_sessions_by_url = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.is_bounce, @@ -340,7 +340,7 @@ defmodule Plausible.Stats do {first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone) total_sessions_by_url = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.entry_page in ^page_urls, @@ -349,7 +349,7 @@ defmodule Plausible.Stats do ) |> Enum.into(%{}) bounced_sessions_by_url = Repo.all( - from s in Plausible.Session, + from s in Plausible.FingerprintSession, where: s.domain == ^site.domain, where: s.start >= ^first_datetime and s.start < ^last_datetime, where: s.is_bounce, @@ -381,7 +381,7 @@ defmodule Plausible.Stats do def top_screen_sizes(site, query) do Repo.all(from e in base_query(site, query), - select: %{name: e.screen_size, count: count(e.user_id, :distinct)}, + select: %{name: e.screen_size, count: count(e.fingerprint, :distinct)}, group_by: e.screen_size, where: not is_nil(e.screen_size) ) @@ -395,7 +395,7 @@ defmodule Plausible.Stats do def countries(site, query) do Repo.all(from e in base_query(site, query), - select: %{name: e.country_code, count: count(e.user_id, :distinct)}, + select: %{name: e.country_code, count: count(e.fingerprint, :distinct)}, group_by: e.country_code, where: not is_nil(e.country_code), order_by: [desc: 2] @@ -411,7 +411,7 @@ defmodule Plausible.Stats do def browsers(site, query, limit \\ 5) do Repo.all(from e in base_query(site, query), - select: %{name: e.browser, count: count(e.user_id, :distinct)}, + select: %{name: e.browser, count: count(e.fingerprint, :distinct)}, group_by: e.browser, where: not is_nil(e.browser), order_by: [desc: 2] @@ -422,7 +422,7 @@ defmodule Plausible.Stats do def operating_systems(site, query, limit \\ 5) do Repo.all(from e in base_query(site, query), - select: %{name: e.operating_system, count: count(e.user_id, :distinct)}, + select: %{name: e.operating_system, count: count(e.fingerprint, :distinct)}, group_by: e.operating_system, where: not is_nil(e.operating_system), order_by: [desc: 2] @@ -436,13 +436,13 @@ defmodule Plausible.Stats do from e in Plausible.Event, where: e.timestamp >= fragment("(now() at time zone 'utc') - '5 minutes'::interval"), where: e.domain == ^site.domain, - select: count(e.user_id, :distinct) + select: count(e.fingerprint, :distinct) ) end def goal_conversions(site, %Query{filters: %{"goal" => goal}} = query) when is_binary(goal) do Repo.all(from e in base_query(site, query), - select: count(e.user_id, :distinct), + select: count(e.fingerprint, :distinct), group_by: e.name, order_by: [desc: 1] ) |> Enum.map(fn count -> %{name: goal, count: count} end) @@ -463,7 +463,7 @@ defmodule Plausible.Stats do Repo.all( from e in base_query(site, query, events), group_by: e.name, - select: %{name: e.name, count: count(e.user_id, :distinct)} + select: %{name: e.name, count: count(e.fingerprint, :distinct)} ) else [] @@ -479,7 +479,7 @@ defmodule Plausible.Stats do from e in base_query(site, query), where: e.pathname in ^pages, group_by: e.pathname, - select: %{name: fragment("concat('Visit ', ?)", e.pathname), count: count(e.user_id, :distinct)} + select: %{name: fragment("concat('Visit ', ?)", e.pathname), count: count(e.fingerprint, :distinct)} ) else [] diff --git a/lib/plausible_web/controllers/api/external_controller.ex b/lib/plausible_web/controllers/api/external_controller.ex index 1b95e2f91..df53c38b5 100644 --- a/lib/plausible_web/controllers/api/external_controller.ex +++ b/lib/plausible_web/controllers/api/external_controller.ex @@ -9,9 +9,7 @@ defmodule PlausibleWeb.Api.ExternalController do {:ok, nil} -> conn |> send_resp(202, "") {:ok, event} -> - Plausible.Ingest.Session.on_event(event) Plausible.Ingest.FingerprintSession.on_event(event) - update_fingerprint(event) conn |> send_resp(202, "") {:error, changeset} -> request = Sentry.Plug.build_request_interface_data(conn, []) @@ -23,7 +21,6 @@ defmodule PlausibleWeb.Api.ExternalController do def unload(conn, _params) do params = parse_body(conn) - Plausible.Ingest.Session.on_unload(params["uid"], Timex.now()) fingerprint = calculate_fingerprint(conn, params) Plausible.Ingest.FingerprintSession.on_unload(fingerprint, Timex.now()) conn |> send_resp(202, "") @@ -35,13 +32,6 @@ defmodule PlausibleWeb.Api.ExternalController do send_resp(conn, 200, "") end - defp update_fingerprint(%Plausible.Event{new_visitor: true}), do: nil - defp update_fingerprint(event) do - use Plausible.Repo - q = from(e in Plausible.Event, where: e.user_id == ^event.user_id and is_nil(e.fingerprint)) - Repo.update_all(q, set: [fingerprint: event.fingerprint]) - end - defp create_event(conn, params) do uri = URI.parse(params["url"]) country_code = Plug.Conn.get_req_header(conn, "cf-ipcountry") |> List.first diff --git a/lib/plausible_web/templates/layout/_tracking.html.eex b/lib/plausible_web/templates/layout/_tracking.html.eex index 14b177d9e..fe6337991 100644 --- a/lib/plausible_web/templates/layout/_tracking.html.eex +++ b/lib/plausible_web/templates/layout/_tracking.html.eex @@ -1,4 +1,4 @@ -<%= if Mix.env() == :prod && !@conn.assigns[:skip_plausible_tracking] do %> - +<%= if !@conn.assigns[:skip_plausible_tracking] do %> + <% end %> diff --git a/priv/repo/migrations/20200317142459_backfill_fingerprints.exs b/priv/repo/migrations/20200317142459_backfill_fingerprints.exs new file mode 100644 index 000000000..ed22a321e --- /dev/null +++ b/priv/repo/migrations/20200317142459_backfill_fingerprints.exs @@ -0,0 +1,14 @@ +defmodule Plausible.Repo.Migrations.BackfillFingerprints do + use Ecto.Migration + + def change do + execute "UPDATE events set fingerprint=user_id where fingerprint is null" + + execute """ + INSERT INTO fingerprint_sessions (hostname, domain, fingerprint, start, length, is_bounce, entry_page, exit_page, referrer, referrer_source, country_code, screen_size, operating_system, browser, timestamp) + SELECT hostname, domain, user_id, start, length, is_bounce, entry_page, exit_page, referrer, referrer_source, country_code, screen_size, operating_system, browser, timestamp + FROM sessions + WHERE sessions.timestamp < '2020-02-27 11:40:55'; + """ + end +end diff --git a/test/plausible/ingest/fingerprint_session_test.exs b/test/plausible/ingest/fingerprint_session_test.exs new file mode 100644 index 000000000..2421034ee --- /dev/null +++ b/test/plausible/ingest/fingerprint_session_test.exs @@ -0,0 +1,80 @@ +defmodule Plausible.Ingest.FingerprintSessionTest do + use Plausible.DataCase + alias Plausible.Ingest + + defp capture_session(fingerprint) do + session_pid = :global.whereis_name(fingerprint) + Process.monitor(session_pid) + + assert_receive({:DOWN, session_pid, :process, _, :normal}) + + Repo.one(Plausible.FingerprintSession) + end + + describe "on_event/1" do + test "starts a new session if there is no session for user id" do + pageview = insert(:pageview) + + refute is_pid(:global.whereis_name(pageview.fingerprint)) + + Ingest.FingerprintSession.on_event(pageview) + + assert is_pid(:global.whereis_name(pageview.fingerprint)) + end + + test "copies event data to session" do + pageview = insert(:pageview) + + Ingest.FingerprintSession.on_event(pageview) + + session = capture_session(pageview.fingerprint) + + assert session.fingerprint == pageview.fingerprint + assert session.start == pageview.timestamp + end + + test "inserts bounced session when timeout fires after one pageview" do + pageview = insert(:pageview) + + Ingest.FingerprintSession.on_event(pageview) + + session = capture_session(pageview.fingerprint) + assert session.is_bounce + end + + test "session with two events is not a bounce" do + pageview = insert(:pageview) + pageview2 = insert(:pageview, fingerprint: pageview.fingerprint) + + Ingest.FingerprintSession.on_event(pageview) + Ingest.FingerprintSession.on_event(pageview2) + + session = capture_session(pageview.fingerprint) + refute session.is_bounce + end + + test "captures the exit page" do + pageview = insert(:pageview) + pageview2 = insert(:pageview, fingerprint: pageview.fingerprint, pathname: "/exit") + + Ingest.FingerprintSession.on_event(pageview) + Ingest.FingerprintSession.on_event(pageview2) + + session = capture_session(pageview.fingerprint) + assert session.exit_page == "/exit" + end + end + + describe "on_unload/1" do + test "uses the unload timestamp to calculate session length" do + pageview = insert(:pageview) + unload_timestamp = Timex.shift(pageview.timestamp, seconds: 30) + + Ingest.FingerprintSession.on_event(pageview) + Ingest.FingerprintSession.on_unload(pageview.fingerprint, unload_timestamp) + + session = capture_session(pageview.fingerprint) + assert session.length == 30 + end + end +end diff --git a/test/plausible_web/controllers/api/external_controller_test.exs b/test/plausible_web/controllers/api/external_controller_test.exs index 7852fcf41..ec3757cfe 100644 --- a/test/plausible_web/controllers/api/external_controller_test.exs +++ b/test/plausible_web/controllers/api/external_controller_test.exs @@ -2,8 +2,8 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do use PlausibleWeb.ConnCase use Plausible.Repo - defp finalize_session(user_id) do - session_pid = :global.whereis_name(user_id) + defp finalize_session(fingerprint) do + session_pid = :global.whereis_name(fingerprint) Process.monitor(session_pid) assert_receive({:DOWN, session_pid, _, _, _}) @@ -12,28 +12,6 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do @user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36" @country_code "EE" - test "TEMP - adding historical fingerprints", %{conn: conn} do - uid = UUID.uuid4() - insert(:pageview, domain: "gigride.live", user_id: uid, new_visitor: true) - - params = %{ - name: "pageview", - url: "http://gigride.live/", - new_visitor: false, - uid: uid - } - - conn - |> put_req_header("content-type", "text/plain") - |> put_req_header("user-agent", @user_agent) - |> post("/api/event", Jason.encode!(params)) - - [pageview1, pageview2] = Repo.all(Plausible.Event) - finalize_session(uid) - - assert pageview1.fingerprint == pageview2.fingerprint - end - describe "POST /api/event" do test "records the event", %{conn: conn} do params = %{ @@ -52,7 +30,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.hostname == "gigride.live" @@ -77,7 +55,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) event = Repo.one(Plausible.Event) - finalize_session(event.user_id) + finalize_session(event.fingerprint) assert response(conn, 202) == "" assert event.domain == "some_site.com" @@ -97,7 +75,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert pageview.domain == "some_site.com" end @@ -115,7 +93,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert pageview.hostname == "example.com" end @@ -151,7 +129,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.operating_system == "Mac" @@ -174,7 +152,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer_source == "Facebook" @@ -197,7 +175,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer == "facebook.com/page" @@ -222,7 +200,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer_source == nil @@ -245,7 +223,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer_source == nil @@ -268,7 +246,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer_source == "blog.gigride.live" @@ -290,7 +268,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert pageview.referrer == "indiehackers.com/page" assert pageview.initial_referrer == "indiehackers.com" @@ -312,7 +290,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert pageview.referrer_source == "betalist" assert pageview.initial_referrer_source == "betalist" @@ -333,7 +311,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.referrer_source == "indiehackers.com" @@ -354,7 +332,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert is_nil(pageview.referrer_source) @@ -377,7 +355,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.screen_size == "Mobile" @@ -397,7 +375,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) pageview = Repo.one(Plausible.Event) - finalize_session(pageview.user_id) + finalize_session(pageview.fingerprint) assert response(conn, 202) == "" assert pageview.screen_size == nil @@ -417,7 +395,7 @@ defmodule PlausibleWeb.Api.ExternalControllerTest do |> post("/api/event", Jason.encode!(params)) event = Repo.one(Plausible.Event) - finalize_session(event.user_id) + finalize_session(event.fingerprint) assert response(conn, 202) == "" assert event.name == "custom event" diff --git a/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs b/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs index 5b1d24c33..1add73072 100644 --- a/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/current_visitors_test.exs @@ -8,7 +8,7 @@ defmodule PlausibleWeb.Api.StatsController.CurrentVisitorsTest do test "returns unique users in the last 5 minutes", %{conn: conn, site: site} do insert(:pageview, domain: site.domain) event2 = insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -3)) - insert(:pageview, domain: site.domain, user_id: event2.user_id, timestamp: Timex.now() |> Timex.shift(minutes: -4)) + insert(:pageview, domain: site.domain, fingerprint: event2.fingerprint, timestamp: Timex.now() |> Timex.shift(minutes: -4)) insert(:pageview, domain: site.domain, timestamp: Timex.now() |> Timex.shift(minutes: -6)) conn = get(conn, "/api/stats/#{site.domain}/current-visitors?period=day&date=2019-01-01") diff --git a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs index cc2306c4a..50a7dae9e 100644 --- a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs @@ -88,8 +88,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do setup [:create_user, :log_in, :create_site] test "unique users counts distinct user ids", %{conn: conn, site: site} do - insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00]) - insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00]) + insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 00:00:00]) + insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 23:59:00]) conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01") @@ -108,8 +108,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do end test "counts total pageviews even from same user ids", %{conn: conn, site: site} do - insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 00:00:00]) - insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 23:59:00]) + insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 00:00:00]) + insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 23:59:00]) conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01") @@ -160,8 +160,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do test "returns total unique visitors", %{conn: conn, site: site} do insert(:pageview, domain: site.domain, timestamp: ~N[2019-01-01 02:00:00]) - insert(:pageview, domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 01:00:00]) - insert(:event, name: "Signup", domain: site.domain, user_id: @user_id, timestamp: ~N[2019-01-01 02:00:00]) + insert(:pageview, domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 01:00:00]) + insert(:event, name: "Signup", domain: site.domain, fingerprint: @user_id, timestamp: ~N[2019-01-01 02:00:00]) filters = Jason.encode!(%{goal: "Signup"}) conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=day&date=2019-01-01&filters=#{filters}") diff --git a/test/plausible_web/controllers/api/stats_controller/referrers_test.exs b/test/plausible_web/controllers/api/stats_controller/referrers_test.exs index 7c9196429..54ab874ce 100644 --- a/test/plausible_web/controllers/api/stats_controller/referrers_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/referrers_test.exs @@ -7,7 +7,7 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do test "returns top referrer sources by user ids", %{conn: conn, site: site} do pageview1 = insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00]) - insert(:pageview, domain: site.domain, referrer_source: "Google", user_id: pageview1.user_id, timestamp: ~N[2019-01-01 02:00:00]) + insert(:pageview, domain: site.domain, referrer_source: "Google", fingerprint: pageview1.fingerprint, timestamp: ~N[2019-01-01 02:00:00]) insert(:pageview, domain: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00]) insert(:pageview, domain: site.domain, referrer_source: "Bing", timestamp: ~N[2019-01-01 02:00:00]) diff --git a/test/support/factory.ex b/test/support/factory.ex index 974c587ca..1bf386bee 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -26,12 +26,11 @@ defmodule Plausible.Factory do def session_factory do hostname = sequence(:domain, &"example-#{&1}.com") - %Plausible.Session{ + %Plausible.FingerprintSession{ hostname: hostname, domain: hostname, - new_visitor: true, entry_page: "/", - user_id: UUID.uuid4(), + fingerprint: UUID.uuid4(), start: Timex.now(), is_bounce: false } @@ -53,7 +52,9 @@ defmodule Plausible.Factory do hostname: hostname, domain: hostname, pathname: "/", - new_visitor: true, user_id: UUID.uuid4(), + new_visitor: true, + user_id: UUID.uuid4(), + fingerprint: UUID.uuid4() } end