towerops/lib
Graham McIntire 075106932c dialyzer: remove all @dialyzer suppressions, refactor Ecto.Multi → Repo.transaction
The remaining @dialyzer {:no_opaque, ...} / {:nowarn_function, ...}
directives in accounts.ex, devices.ex, organizations.ex all masked the
same upstream issue: Ecto.Multi.new/0 produces a struct containing a
literal %MapSet{map: %{}} whose concrete shape violates MapSet's
@opaque t at every subsequent Ecto.Multi.* call boundary.

Tried first: public helper function with @spec Ecto.Multi.t() — confirmed
dialyzer uses success typing of the body, not the spec. Doesn't help.

Real fix: drop Ecto.Multi entirely for these transactions. Each was
performing a small, sequential set of Repo operations that map cleanly
to a plain `Repo.transaction(fn -> ... end)` block with Repo.rollback
on errors. No Multi machinery is actually needed:

- accounts.ex:
  - register_user/1: insert + 0-2 consent grants
  - register_user_with_organization/1: insert + create_org + consents
  - confirm_user_transaction/1: update + delete_all tokens
- organizations.ex:
  - do_create_organization/4: lock user → limit check → insert org + membership
  - set_default_organization/2: two update_alls
  - accept_invitation/2: update invitation + insert membership
- devices.ex:
  - create_device/2: maybe lock org → maybe quota check → insert

Caught a real consent-failure rollback bug along the way: initial
refactor discarded the return of grant_consents_sequential/2, which
meant FK violations on consent insert would abort the transaction at
the Postgres level but the caller still saw {:ok, user} — later
operations in the transaction silently failed with 25P02. Fixed by
propagating the {:error, changeset} up so Repo.rollback is called.
Verified by the AccountsDefensiveCoverageTest regression tests.

Behavior is preserved — same transactional guarantees, same success
/ error tuple shapes. Net -86 LOC and no suppressions.

mix dialyzer: `done (passed successfully)` with zero @dialyzer
directives in lib/.
2026-04-21 13:04:03 -05:00
..
mix/tasks dialyzer: fix remaining 88 warnings — clean dialyzer run 2026-04-21 10:32:42 -05:00
snmpkit refactor: convert 6 Gleam modules to idiomatic Elixir with TDD (#196) 2026-03-28 09:52:07 -05:00
towerops dialyzer: remove all @dialyzer suppressions, refactor Ecto.Multi → Repo.transaction 2026-04-21 13:04:03 -05:00
towerops_web dialyzer: replace suppressions with real fixes where possible 2026-04-21 11:01:30 -05:00
snmp_lib.ex bring in snmpkit 2026-01-23 12:52:17 -06:00
snmp_mgr.ex credo fixes 2026-01-23 14:01:52 -06:00
snmpkit.ex complete overhaul of snmp engine 2026-01-30 10:41:07 -06:00
towerops.ex init 2025-12-21 11:10:43 -06:00
towerops_native.ex dialyzer: replace suppressions with real fixes where possible 2026-04-21 11:01:30 -05:00
towerops_web.ex i18n: wrap all user-facing strings in gettext() 2026-02-14 17:44:01 -06:00