fix: allow nullable fields to be set to null via PATCH request#1783
fix: allow nullable fields to be set to null via PATCH request#1783mrcavisg wants to merge 2 commits intoLerianStudio:mainfrom
Conversation
Previously, sending null for segmentId, portfolioId, or entityId in Account PATCH requests was ignored, preventing users from unlinking accounts from segments. This change introduces a nullable package that distinguishes between "field not provided" and "field explicitly set to null" in JSON payloads. The nullable package can be reused for other entities with similar fields. Closes LerianStudio#1778 Signed-off-by: mrcavisg <caio.vinicius.contato@gmail.com>
WalkthroughThis pull request adds a generic Nullable[T] type to represent three JSON states (unset, null, value) and updates account input types to use nullable.Nullable[string] for SegmentID, PortfolioID, and EntityID. The account repository Update signature now accepts mmodel.UpdateAccountInput instead of a full Account, and the implementation applies conditional SQL updates based on Nullable semantics (ShouldUpdate / ShouldSetNull), sets updated_at, and returns the fresh record via a follow-up find query. Mock and service call sites were updated to pass the new UpdateAccountInput. Sequence Diagram(s)sequenceDiagram
participant Client
participant Service as Service Layer
participant Repo as Repository
participant DB as Database
Client->>Service: UpdateAccount(ctx, UpdateAccountInput)
Service->>Repo: Update(ctx, orgID, ledgerID, portfolioID, id, UpdateAccountInput)
Repo->>Repo: Evaluate Nullable fields (ShouldUpdate / ShouldSetNull)
Repo->>DB: EXECUTE UPDATE ... SET updated_at = now(), field = value/NULL ... WHERE id=?
DB-->>Repo: OK
Repo->>DB: SELECT * FROM account WHERE id=?
DB-->>Repo: account row
Repo-->>Service: *Account (fresh)
Service-->>Client: Updated Account response
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
This is my first contribution to this project. I noticed the nullable package was already designed for this issue, so I used the existing infrastructure. Happy to adjust anything based on feedback. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@pkg/mmodel/account.go`:
- Around line 115-129: The nullable.Nullable[string] fields SegmentID,
PortfolioID, and EntityID currently carry validator tags (e.g.,
`validate:"omitempty,uuid"`) which won't work because the validator sees the
Nullable struct, not the inner string; register a custom type extractor with the
validator (e.g., using RegisterCustomTypeFunc) that returns the inner string
only when the Nullable is set and not null, or implement a custom validation
rule via RegisterValidation that inspects Nullable.IsSet/IsNull and then
validates Nullable.Value against uuid/max constraints so the existing tags on
SegmentID, PortfolioID and EntityID behave correctly.
In `@pkg/nullable/nullable.go`:
- Around line 69-76: MarshalJSON currently serializes unset fields (Nullable[T]
with IsSet=false) as JSON null which causes absent fields to appear as null on
re-marshal; implement an IsZero() bool method on Nullable[T] that returns
!n.IsSet so the type can be omitted via json:",omitzero" or omitempty semantics
(Go 1.24+ or struct tags) instead of changing MarshalJSON behavior — add IsZero
to the Nullable[T] type so callers can rely on omitzero/omitempty when they want
unset fields omitted.
Register a custom type function in the validator to extract the inner string value from nullable.Nullable[string] fields. This ensures that validation tags (uuid, max) work correctly on SegmentID, PortfolioID, and EntityID fields. Signed-off-by: mrcavisg <caio.vinicius.contato@gmail.com>
Summary
Fixes #1778
When sending a PATCH request with
"segmentId": null(orportfolioId,entityId), the null value was beingignored and the field remained unchanged. This prevented users from unlinking an account from a segment/portfolio.
Root Cause
Go cannot distinguish between "field omitted from JSON" and "field explicitly set to null" when using pointer types -
both result in
nil.Solution
nullable.Nullable[T]generic type (already in the codebase atpkg/nullable/) which trackswhether a field was present in the JSON payload
UpdateAccountInputto usenullable.Nullable[string]forsegmentId,portfolioId, andentityIdUpdatemethod to conditionally build the SQL query based on which fields were actuallysent
Changes
pkg/mmodel/account.go- Changed nullable field types inUpdateAccountInputcomponents/onboarding/.../account.postgresql.go- UpdatedUpdatemethod to handle nullable fieldsTest Plan
"segmentId": null→ segmentId correctly set to null{}(empty) → segmentId unchangedNotes
This fix only addresses the
Accountentity. If approved, the same pattern can be applied to other entities(
Organization,Alias,Holder) that have similar nullable fields.