Reapply sentry context (#3675)

* Reapply "Sentry context in live views (#3672)"

This reverts commit 5449fead160064b8a0081c458cc5dcd34399eb0b.

* Make sure `:selection_made` is handled in `GoalSettings.Form`

That was a bit unepexcted.. normally `handle_info` is injected
by the LiveView use macro and it discards any message gracefully.
After switching to `use PlausibleWeb, :live_view` we're also
using `PlausibleWeb.Live.Flash` that happens to inject its own receive
clause for closing the flash. Which then renders the original,
overridable, `handle_info` catch-all obsolete.

* Update LV SentryContext only on connected sockets

(first mount already has the right context coming from Sentry plug)

* Make sure Live.ChoosePlan passes `current_user_id` session key
This commit is contained in:
hq1 2024-01-09 12:28:31 +01:00 committed by GitHub
parent c87b165aef
commit 24a8aa2821
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 207 additions and 29 deletions

View File

@ -2,9 +2,8 @@ defmodule PlausibleWeb.Live.FunnelSettings do
@moduledoc """
LiveView allowing listing, creating and deleting funnels.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
use PlausibleWeb.Live.Flash
use Plausible.Funnel

View File

@ -5,7 +5,7 @@ defmodule PlausibleWeb.Live.FunnelSettings.Form do
to allow building searchable funnel definitions out of list of goals available.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Plausible.Funnel
import PlausibleWeb.Live.Components.Form

View File

@ -1,4 +1,19 @@
defmodule PlausibleWeb do
def live_view(opts \\ []) do
quote do
use Plausible
use Phoenix.LiveView, global_prefixes: ~w(x-)
use PlausibleWeb.Live.Flash
unless :no_sentry_context in unquote(opts) do
use PlausibleWeb.Live.SentryContext
end
alias PlausibleWeb.Router.Helpers, as: Routes
alias Phoenix.LiveView.JS
end
end
def controller do
quote do
use Phoenix.Controller, namespace: PlausibleWeb
@ -86,4 +101,8 @@ defmodule PlausibleWeb do
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
defmacro __using__([{which, opts}]) when is_atom(which) do
apply(__MODULE__, which, [List.wrap(opts)])
end
end

View File

@ -17,7 +17,12 @@ defmodule PlausibleWeb.Endpoint do
socket("/live", Phoenix.LiveView.Socket,
websocket: [
check_origin: true,
connect_info: [session: {__MODULE__, :runtime_session_opts, []}]
connect_info: [
:peer_data,
:uri,
:user_agent,
session: {__MODULE__, :runtime_session_opts, []}
]
]
)

View File

@ -2,7 +2,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
@moduledoc """
LiveView for upgrading to a plan, or changing an existing plan.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
require Plausible.Billing.Subscription.Status
@ -15,7 +15,7 @@ defmodule PlausibleWeb.Live.ChoosePlan do
@contact_link "https://plausible.io/contact"
@billing_faq_link "https://plausible.io/docs/billing"
def mount(_params, %{"user_id" => user_id}, socket) do
def mount(_params, %{"current_user_id" => user_id}, socket) do
socket =
socket
|> assign_new(:user, fn ->

View File

@ -2,9 +2,8 @@ defmodule PlausibleWeb.Live.GoalSettings do
@moduledoc """
LiveView allowing listing, creating and deleting goals.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
use PlausibleWeb.Live.Flash
alias Plausible.{Sites, Goals}

View File

@ -2,8 +2,7 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
@moduledoc """
Live view for the goal creation form
"""
use Phoenix.LiveView
use Plausible
use PlausibleWeb, :live_view
import PlausibleWeb.Live.Components.Form
alias PlausibleWeb.Live.Components.ComboBox
@ -269,6 +268,10 @@ defmodule PlausibleWeb.Live.GoalSettings.Form do
"""
end
def handle_info({:selection_made, %{submit_value: _prop}}, socket) do
{:noreply, socket}
end
def handle_event("switch-tab", _params, socket) do
{:noreply,
assign(socket,

View File

@ -2,9 +2,9 @@ defmodule PlausibleWeb.Live.Plugins.API.Settings do
@moduledoc """
LiveView allowing listing, creating and revoking Plugins API tokens.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
use PlausibleWeb.Live.Flash
alias Plausible.Sites
alias Plausible.Plugins.API.Tokens

View File

@ -2,7 +2,7 @@ defmodule PlausibleWeb.Live.Plugins.API.TokenForm do
@moduledoc """
Live view for the goal creation form
"""
use Phoenix.LiveView
use PlausibleWeb, live_view: :no_sentry_context
import PlausibleWeb.Live.Components.Form
alias Plausible.Repo

View File

@ -3,9 +3,8 @@ defmodule PlausibleWeb.Live.PropsSettings do
LiveView allowing listing, allowing and disallowing custom event properties.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
use PlausibleWeb.Live.Flash
alias PlausibleWeb.Live.Components.ComboBox

View File

@ -2,7 +2,7 @@ defmodule PlausibleWeb.Live.PropsSettings.Form do
@moduledoc """
Live view for the custom props creation form
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
import PlausibleWeb.Live.Components.Form
alias PlausibleWeb.Live.Components.ComboBox

View File

@ -3,17 +3,13 @@ defmodule PlausibleWeb.Live.RegisterForm do
LiveView for registration form.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
use Plausible
import PlausibleWeb.Live.Components.Form
alias Plausible.Auth
alias Plausible.Repo
alias PlausibleWeb.Router.Helpers, as: Routes
def mount(params, _session, socket) do
socket =
assign_new(socket, :invitation, fn ->

View File

@ -3,7 +3,7 @@ defmodule PlausibleWeb.Live.ResetPasswordForm do
LiveView for password reset form.
"""
use Phoenix.LiveView
use PlausibleWeb, :live_view
use Phoenix.HTML
import PlausibleWeb.Live.Components.Form

View File

@ -0,0 +1,78 @@
defmodule PlausibleWeb.Live.SentryContext do
@moduledoc """
This module tries to supply LiveViews with some common Sentry context
(without it, there is practically none).
Use via `use PlausibleWeb.Live.SentryContext` in your LiveView module,
or preferably via `use PlausibleWeb, :live_view`.
In case you have multiple LiveViews, there is `use PlausibleWeb, live_view: :no_sentry_context`
exposed that allows you to skip using this module. This is because
only the root LiveView has access to `connect_info` and an exception will be
thrown otherwise.
"""
defmacro __using__(_) do
quote do
on_mount PlausibleWeb.Live.SentryContext
end
end
def on_mount(:default, _params, session, socket) do
if Phoenix.LiveView.connected?(socket) do
peer = Phoenix.LiveView.get_connect_info(socket, :peer_data)
uri = Phoenix.LiveView.get_connect_info(socket, :uri)
user_agent =
Phoenix.LiveView.get_connect_info(socket, :user_agent)
socket_host =
case socket.host_uri do
:not_mounted_at_router -> :not_mounted_at_router
%URI{host: host} -> host
end
request_context =
%{
host: socket_host,
env: %{
"REMOTE_ADDR" => get_ip(peer),
"REMOTE_PORT" => peer && peer.port,
"SEVER_NAME" => uri && uri.host
}
}
request_context =
if user_agent do
Map.merge(request_context, %{
headers: %{
"User-Agent" => user_agent
}
})
else
request_context
end
Sentry.Context.set_request_context(request_context)
user_id = session["current_user_id"]
if user_id do
Sentry.Context.set_user_context(%{
id: user_id
})
end
end
{:cont, socket}
end
defp get_ip(%{address: addr}) do
case :inet.ntoa(addr) do
{:error, _} -> ""
address -> to_string(address)
end
end
defp get_ip(_), do: ""
end

View File

@ -3,11 +3,7 @@ defmodule PlausibleWeb.Live.Sites do
LiveView for sites index.
"""
use Phoenix.LiveView, global_prefixes: ~w(x-)
use PlausibleWeb.Live.Flash
use Plausible
alias Phoenix.LiveView.JS
use PlausibleWeb, :live_view
use Phoenix.HTML
import PlausibleWeb.Components.Generic
@ -18,7 +14,6 @@ defmodule PlausibleWeb.Live.Sites do
alias Plausible.Site
alias Plausible.Sites
alias Plausible.Site.Memberships.Invitations
alias PlausibleWeb.Router.Helpers, as: Routes
def mount(params, %{"current_user_id" => user_id}, socket) do
uri =

View File

@ -1,4 +1,4 @@
<%= live_render(@conn, PlausibleWeb.Live.ChoosePlan,
id: "choose-plan",
session: %{"user_id" => @user.id}
session: %{"current_user_id" => @user.id}
) %>

View File

@ -129,6 +129,13 @@ defmodule PlausibleWeb.Live.GoalSettings.FormTest do
refute element_exists?(html, ~s/a[phx-value-display-value="PLN - Polish Zloty"]/)
assert element_exists?(html, ~s/a[phx-value-display-value="EUR - Euro"]/)
lv
|> element("#dropdown-currency_input-option-1 a")
|> render_click()
# make sure combo's {:selection_made, ...} message is received
render(lv)
end
test "pageview combo works", %{conn: conn, site: site} do

View File

@ -0,0 +1,78 @@
defmodule PlausibleWeb.Live.SentryContextTest do
use PlausibleWeb.ConnCase, async: true
import Phoenix.LiveViewTest
defmodule SampleLV do
use PlausibleWeb, :live_view
def mount(_params, %{"test" => test_pid}, socket) do
socket = assign(socket, test_pid: test_pid)
{:ok, socket}
end
def render(assigns) do
~H"""
ok computer
"""
end
def handle_event("get_sentry_context", _params, socket) do
context = Sentry.Context.get_all()
send(socket.assigns.test_pid, {:context, context})
{:noreply, socket}
end
end
describe "sentry context" do
test "basic shape", %{conn: conn} do
context_hook(conn)
assert_receive {:context, context}
assert %{
extra: %{},
request: %{
env: %{
"REMOTE_ADDR" => "127.0.0.1",
"REMOTE_PORT" => port,
"SEVER_NAME" => "www.example.com"
},
host: :not_mounted_at_router
},
user: %{},
tags: %{},
breadcrumbs: []
} = context
assert is_integer(port)
end
test "user-agent is included", %{conn: conn} do
conn
|> put_req_header("user-agent", "Firefox")
|> context_hook()
assert_receive {:context, context}
assert context.request.headers["User-Agent"] == "Firefox"
end
test "user_id is included", %{conn: conn} do
context_hook(conn, %{"current_user_id" => 172})
assert_receive {:context, context}
assert context.user.id == 172
end
end
defp context_hook(conn, extra_session \\ %{}) do
lv = get_liveview(conn, extra_session)
assert render(lv) =~ "ok computer"
render_hook(lv, :get_sentry_context, %{})
end
defp get_liveview(conn, extra_session) do
{:ok, lv, _html} =
live_isolated(conn, SampleLV, session: Map.merge(%{"test" => self()}, extra_session))
lv
end
end