Fix Credo Design (AliasUsage): add aliases in lib

Add module aliases at top and use short names instead of
fully qualified nested modules across lib/.
This commit is contained in:
Moritz 2026-03-03 19:03:47 +01:00
parent cfc8900c5c
commit 7a8b069834
Signed by: moritz
GPG key ID: 1020A035E5DD0824
25 changed files with 176 additions and 109 deletions

View file

@ -18,15 +18,19 @@ defmodule MvWeb.LinkOidcAccountLive do
require Ash.Query
require Logger
alias AshAuthentication.Strategy.Password.Actions, as: PasswordActions
alias Mv.Accounts.User, as: UserResource
alias Mv.Helpers.SystemActor
@impl true
def mount(_params, session, socket) do
# Use SystemActor for authorization during OIDC linking (user is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
system_actor = SystemActor.get_system_actor()
with user_id when not is_nil(user_id) <- Map.get(session, "oidc_linking_user_id"),
oidc_user_info when not is_nil(oidc_user_info) <-
Map.get(session, "oidc_linking_user_info"),
{:ok, user} <- Ash.get(Mv.Accounts.User, user_id, actor: system_actor) do
{:ok, user} <- Ash.get(UserResource, user_id, actor: system_actor) do
# Check if user is passwordless
if passwordless?(user) do
# Auto-link passwordless user immediately
@ -50,9 +54,9 @@ defmodule MvWeb.LinkOidcAccountLive do
defp reload_user!(user_id) do
# Use SystemActor for authorization during OIDC linking (user is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
system_actor = SystemActor.get_system_actor()
Mv.Accounts.User
UserResource
|> Ash.Query.filter(id == ^user_id)
|> Ash.read_one!(actor: system_actor)
end
@ -65,7 +69,7 @@ defmodule MvWeb.LinkOidcAccountLive do
oidc_id = Map.get(oidc_user_info, "sub") || Map.get(oidc_user_info, "id")
# Use SystemActor for authorization (passwordless user auto-linking)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
system_actor = SystemActor.get_system_actor()
case user.id
|> reload_user!()
@ -176,11 +180,11 @@ defmodule MvWeb.LinkOidcAccountLive do
defp verify_password(email, password) do
# Use AshAuthentication password strategy to verify
strategies = AshAuthentication.Info.authentication_strategies(Mv.Accounts.User)
strategies = AshAuthentication.Info.authentication_strategies(UserResource)
password_strategy = Enum.find(strategies, fn s -> s.name == :password end)
if password_strategy do
AshAuthentication.Strategy.Password.Actions.sign_in(
PasswordActions.sign_in(
password_strategy,
%{
"email" => email,
@ -197,7 +201,7 @@ defmodule MvWeb.LinkOidcAccountLive do
oidc_id = Map.get(oidc_user_info, "sub") || Map.get(oidc_user_info, "id")
# Use SystemActor for authorization (user just verified password but is not yet logged in)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
system_actor = SystemActor.get_system_actor()
# Update the user with the OIDC ID
case user.id

View file

@ -12,11 +12,13 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do
"""
use MvWeb, :live_component
alias MvWeb.Translations.FieldTypes
require Logger
@impl true
def render(assigns) do
assigns = assign(assigns, :field_type_label, &MvWeb.Translations.FieldTypes.label/1)
assigns = assign(assigns, :field_type_label, &FieldTypes.label/1)
~H"""
<div id={@id}>

View file

@ -26,7 +26,11 @@ defmodule MvWeb.GlobalSettingsLive do
require Ash.Query
import Ash.Expr
alias Mv.Helpers
alias Mv.Helpers.SystemActor
alias Mv.Membership
alias Mv.Membership.Member, as: MemberResource
alias MvWeb.Helpers.MemberHelpers
on_mount {MvWeb.LiveHelpers, :ensure_user_role_loaded}
@ -551,13 +555,13 @@ defmodule MvWeb.GlobalSettingsLive do
end
defp fetch_member_names_by_ids(ids) do
actor = Mv.Helpers.SystemActor.get_system_actor()
opts = Mv.Helpers.ash_actor_opts(actor)
query = Ash.Query.filter(Mv.Membership.Member, expr(id in ^ids))
actor = SystemActor.get_system_actor()
opts = Helpers.ash_actor_opts(actor)
query = Ash.Query.filter(MemberResource, expr(id in ^ids))
case Ash.read(query, opts) do
{:ok, members} ->
Map.new(members, fn m -> {m.id, MvWeb.Helpers.MemberHelpers.display_name(m)} end)
Map.new(members, fn m -> {m.id, MemberHelpers.display_name(m)} end)
_ ->
%{}

View file

@ -25,8 +25,11 @@ defmodule MvWeb.MemberLive.Form do
alias Mv.Membership
alias Mv.Membership.Helpers.VisibilityConfig
alias Mv.Membership.Member, as: MemberResource
alias Mv.MembershipFees
alias Mv.MembershipFees.MembershipFeeType
alias Mv.Vereinfacht.SyncFlash
alias MvWeb.Helpers.MemberHelpers
alias MvWeb.Helpers.MembershipFeeHelpers
@impl true
@ -51,7 +54,7 @@ defmodule MvWeb.MemberLive.Form do
</.button>
</:leading>
<%= if @member do %>
{MvWeb.Helpers.MemberHelpers.display_name(@member)}
{MemberHelpers.display_name(@member)}
<% else %>
{gettext("New Member")}
<% end %>
@ -289,7 +292,7 @@ defmodule MvWeb.MemberLive.Form do
data-testid="member-delete"
aria-label={
gettext("Delete member %{name}",
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
name: MemberHelpers.display_name(@member)
)
}
>
@ -316,7 +319,7 @@ defmodule MvWeb.MemberLive.Form do
<p class="py-4">
{gettext(
"Are you sure you want to delete %{name}? This action cannot be undone.",
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
name: MemberHelpers.display_name(@member)
)}
</p>
<div class="modal-action">
@ -371,7 +374,7 @@ defmodule MvWeb.MemberLive.Form do
member =
case params["id"] do
nil -> nil
id -> Ash.get!(Mv.Membership.Member, id, load: [:membership_fee_type], actor: actor)
id -> Ash.get!(MemberResource, id, load: [:membership_fee_type], actor: actor)
end
page_title =
@ -562,7 +565,7 @@ defmodule MvWeb.MemberLive.Form do
end
defp maybe_put_vereinfacht_sync_flash(socket, member_id) do
case Mv.Vereinfacht.SyncFlash.take(to_string(member_id)) do
case SyncFlash.take(to_string(member_id)) do
{:warning, message} ->
put_flash(socket, :warning, translate_vereinfacht_flash(message))
@ -767,7 +770,7 @@ defmodule MvWeb.MemberLive.Form do
)
else
AshPhoenix.Form.for_create(
Mv.Membership.Member,
MemberResource,
:create_member,
api: Mv.Membership,
as: "member",

View file

@ -32,6 +32,7 @@ defmodule MvWeb.MemberLive.Index do
import MvWeb.LiveHelpers, only: [current_actor: 1]
alias Mv.Membership
alias Mv.Membership.Member, as: MemberResource
alias MvWeb.Helpers.DateFormatter
alias MvWeb.MemberLive.Index.FieldSelection
alias MvWeb.MemberLive.Index.FieldVisibility
@ -1012,7 +1013,7 @@ defmodule MvWeb.MemberLive.Index do
defp apply_search_filter(query, search_query) do
if search_query && String.trim(search_query) != "" do
query
|> Mv.Membership.Member.fuzzy_search(%{query: search_query})
|> MemberResource.fuzzy_search(%{query: search_query})
else
query
end

View file

@ -24,7 +24,13 @@ defmodule MvWeb.MemberLive.Show do
import Ash.Query
import MvWeb.LiveHelpers, only: [current_actor: 1]
alias Mv.Membership.CustomField
alias Mv.Membership.CustomFieldValue
alias Mv.Membership.Member, as: MemberResource
alias Mv.Vereinfacht.Client, as: VereinfachtClient
alias MvWeb.Helpers.MemberHelpers
alias MvWeb.Helpers.MembershipFeeHelpers
alias Phoenix.HTML.Engine, as: HTMLEngine
@impl true
def render(assigns) do
@ -41,7 +47,7 @@ defmodule MvWeb.MemberLive.Show do
{gettext("Back")}
</.button>
</:leading>
{MvWeb.Helpers.MemberHelpers.display_name(@member)}
{MemberHelpers.display_name(@member)}
<:actions>
<%= if can?(@current_user, :update, @member) do %>
<.button
@ -329,7 +335,7 @@ defmodule MvWeb.MemberLive.Show do
data-testid="member-delete"
aria-label={
gettext("Delete member %{name}",
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
name: MemberHelpers.display_name(@member)
)
}
>
@ -355,7 +361,7 @@ defmodule MvWeb.MemberLive.Show do
</h3>
<p class="py-4">
{gettext("Are you sure you want to delete %{name}? This action cannot be undone.",
name: MvWeb.Helpers.MemberHelpers.display_name(@member)
name: MemberHelpers.display_name(@member)
)}
</p>
<div class="modal-action">
@ -402,13 +408,13 @@ defmodule MvWeb.MemberLive.Show do
# Load custom fields once using assign_new to avoid repeated queries
socket =
assign_new(socket, :custom_fields, fn ->
Mv.Membership.CustomField
CustomField
|> Ash.Query.sort(name: :asc)
|> Ash.read!(actor: actor)
end)
query =
Mv.Membership.Member
MemberResource
|> filter(id == ^id)
|> load([
:user,
@ -527,7 +533,7 @@ defmodule MvWeb.MemberLive.Show do
def handle_event("load_vereinfacht_receipts", %{"contact_id" => contact_id}, socket) do
response =
case Mv.Vereinfacht.Client.get_contact_with_receipts(contact_id) do
case VereinfachtClient.get_contact_with_receipts(contact_id) do
{:ok, receipts} -> {:ok, receipts}
{:error, reason} -> {:error, reason}
end
@ -717,7 +723,7 @@ defmodule MvWeb.MemberLive.Show do
# Handles both CustomFieldValue structs and direct values
defp format_custom_field_value(nil, _type), do: render_empty_value()
defp format_custom_field_value(%Mv.Membership.CustomFieldValue{} = cfv, value_type) do
defp format_custom_field_value(%CustomFieldValue{} = cfv, value_type) do
format_custom_field_value(cfv.value, value_type)
end
@ -759,6 +765,6 @@ defmodule MvWeb.MemberLive.Show do
# Returns safe HTML so it can be used from helpers without LiveView assigns.
defp render_empty_value do
text = gettext("Not set")
{:safe, ["<span class=\"sr-only\">", Phoenix.HTML.Engine.html_escape(text), "</span>"]}
{:safe, ["<span class=\"sr-only\">", HTMLEngine.html_escape(text), "</span>"]}
end
end

View file

@ -35,7 +35,14 @@ defmodule MvWeb.UserLive.Form do
require Jason
alias Mv.Accounts
alias Mv.Accounts.User, as: UserResource
alias Mv.Authorization
alias Mv.Authorization.Role, as: RoleResource
alias Mv.Helpers.SystemActor
alias Mv.Membership
alias Mv.Membership.Member, as: MemberResource
alias MvWeb.Helpers.MemberHelpers
import MvWeb.LiveHelpers, only: [current_actor: 1, submit_form: 3]
import MvWeb.Authorization, only: [can?: 3]
@ -303,7 +310,7 @@ defmodule MvWeb.UserLive.Form do
<% end %>
<%!-- Danger zone: canonical pattern (same as member form) --%>
<%= if @user && can?(@current_user, :destroy, @user) && !Mv.Helpers.SystemActor.system_user?(@user) do %>
<%= if @user && can?(@current_user, :destroy, @user) && !SystemActor.system_user?(@user) do %>
<section class="mt-8 mb-6" aria-labelledby="danger-zone-heading">
<h2 id="danger-zone-heading" class="text-lg font-semibold mb-3 text-error">
{gettext("Danger zone")}
@ -402,9 +409,9 @@ defmodule MvWeb.UserLive.Form do
defp load_user_or_redirect(nil, _actor, _socket), do: {:ok, nil}
defp load_user_or_redirect(id, actor, socket) do
user = Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts, load: [:member], actor: actor)
user = Ash.get!(UserResource, id, domain: Accounts, load: [:member], actor: actor)
if Mv.Helpers.SystemActor.system_user?(user) do
if SystemActor.system_user?(user) do
{:redirect,
socket
|> put_flash(:error, gettext("This user cannot be edited."))
@ -420,9 +427,9 @@ defmodule MvWeb.UserLive.Form do
page_title = action <> " " <> gettext("User")
# Only admins can link/unlink users to members (permission docs; prevents privilege escalation).
can_manage_member_linking = can?(actor, :destroy, Mv.Accounts.User)
can_manage_member_linking = can?(actor, :destroy, UserResource)
# Only admins can assign user roles (Role update permission).
can_assign_role = can?(actor, :update, Mv.Authorization.Role)
can_assign_role = can?(actor, :update, RoleResource)
roles = if can_assign_role, do: load_roles(actor), else: []
{:ok,
@ -541,7 +548,7 @@ defmodule MvWeb.UserLive.Form do
|> put_flash(:error, gettext("User not found"))
|> assign(:show_delete_modal, false)}
Mv.Helpers.SystemActor.system_user?(user) ->
SystemActor.system_user?(user) ->
{:noreply,
socket
|> put_flash(:error, gettext("System user cannot be deleted."))
@ -634,7 +641,7 @@ defmodule MvWeb.UserLive.Form do
member_name =
if selected_member,
do: MvWeb.Helpers.MemberHelpers.display_name(selected_member),
do: MemberHelpers.display_name(selected_member),
else: ""
# Store the selected member ID and name in socket state and clear unlink flag
@ -704,17 +711,17 @@ defmodule MvWeb.UserLive.Form do
defp perform_member_link_action(socket, user, actor) do
# Only admins may link/unlink (backend policy also restricts update_user; UI must not call it).
if can?(actor, :destroy, Mv.Accounts.User) do
if can?(actor, :destroy, UserResource) do
cond do
# Selected member ID takes precedence (new link)
socket.assigns.selected_member_id ->
Mv.Accounts.update_user(user, %{member: %{id: socket.assigns.selected_member_id}},
Accounts.update_user(user, %{member: %{id: socket.assigns.selected_member_id}},
actor: actor
)
# Unlink flag is set
socket.assigns[:unlink_member] ->
Mv.Accounts.update_user(user, %{member: nil}, actor: actor)
Accounts.update_user(user, %{member: nil}, actor: actor)
# No changes to member relationship
true ->
@ -831,8 +838,8 @@ defmodule MvWeb.UserLive.Form do
# For new users, use password registration if password fields are shown
action = if show_password_fields, do: :register_with_password, else: :create_user
AshPhoenix.Form.for_create(Mv.Accounts.User, action,
domain: Mv.Accounts,
AshPhoenix.Form.for_create(UserResource, action,
domain: Accounts,
as: "user",
actor: actor
)
@ -878,7 +885,7 @@ defmodule MvWeb.UserLive.Form do
search_query_str = if search_query && search_query != "", do: search_query, else: nil
query =
Mv.Membership.Member
MemberResource
|> Ash.Query.for_read(:available_for_linking, %{
user_email: user_email_str,
search_query: search_query_str
@ -890,7 +897,7 @@ defmodule MvWeb.UserLive.Form do
if is_nil(actor) do
[]
else
case Ash.read(query, domain: Mv.Membership, actor: actor) do
case Ash.read(query, domain: Membership, actor: actor) do
{:ok, members} -> apply_email_filter(members, user_email_str)
{:error, _} -> []
end
@ -902,7 +909,7 @@ defmodule MvWeb.UserLive.Form do
defp apply_email_filter(members, nil), do: members
defp apply_email_filter(members, user_email_str) when is_binary(user_email_str) do
Mv.Membership.Member.filter_by_email_match(members, user_email_str)
MemberResource.filter_by_email_match(members, user_email_str)
end
@spec load_roles(any()) :: [Mv.Authorization.Role.t()]

View file

@ -19,6 +19,10 @@ defmodule MvWeb.UserLive.Index do
import MvWeb.LiveHelpers, only: [current_actor: 1]
alias Mv.Accounts
alias Mv.Accounts.User, as: UserResource
alias Mv.Helpers.SystemActor
require Ash.Query
@impl true
@ -26,9 +30,9 @@ defmodule MvWeb.UserLive.Index do
actor = current_actor(socket)
users =
Mv.Accounts.User
|> Ash.Query.filter(email != ^Mv.Helpers.SystemActor.system_user_email())
|> Ash.read!(domain: Mv.Accounts, load: [:member, :role], actor: actor)
UserResource
|> Ash.Query.filter(email != ^SystemActor.system_user_email())
|> Ash.read!(domain: Accounts, load: [:member, :role], actor: actor)
sorted = Enum.sort_by(users, & &1.email)

View file

@ -29,6 +29,10 @@ defmodule MvWeb.UserLive.Show do
import MvWeb.LiveHelpers, only: [current_actor: 1]
import MvWeb.ErrorHelpers, only: [format_ash_error: 1]
alias Mv.Accounts
alias Mv.Accounts.User, as: UserResource
alias Mv.Helpers.SystemActor
@impl true
def render(assigns) do
~H"""
@ -167,9 +171,9 @@ defmodule MvWeb.UserLive.Show do
actor = current_actor(socket)
user =
Ash.get!(Mv.Accounts.User, id, domain: Mv.Accounts, load: [:member, :role], actor: actor)
Ash.get!(UserResource, id, domain: Accounts, load: [:member, :role], actor: actor)
if Mv.Helpers.SystemActor.system_user?(user) do
if SystemActor.system_user?(user) do
{:ok,
socket
|> put_flash(:error, gettext("This user cannot be viewed."))
@ -221,7 +225,7 @@ defmodule MvWeb.UserLive.Show do
|> put_flash(:error, gettext("User not found"))
|> assign(:show_delete_modal, false)}
Mv.Helpers.SystemActor.system_user?(user) ->
SystemActor.system_user?(user) ->
{:noreply,
socket
|> put_flash(:error, gettext("System user cannot be deleted."))