feat: composed attributes with derived fields#3
Open
treere wants to merge 1 commit into
Open
Conversation
Introduces type: :composed for resource attributes that need a bidirezional mapping between one real struct field and multiple derived JSON API fields. Configuration is purely declarative via @derive; transformation logic lives in defimpl JSONAPIPlug.Resource.Attribute following the existing protocol pattern.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
JSONAPIPlug.Resourcesupports only 1:1 attribute mappings: each struct field maps to exactly one JSON API attribute. There is no mechanism for composed attributes — cases where a single stored field needs to be exposed as multiple fields in the API, or conversely, where multiple incoming fields need to be composed into one value before reaching the changeset.A common example: a database stores
full_name: "Mario/Rossi"but the API should expose and acceptnomeandcognomeas separate fields.Solution
A new
type: :composedoption for attributes, paired withcomposed_of: [...]to declare which derived fields participate in the mapping.Configuration (declarative, in
@derive)Transformation logic (in
defimpl, following the existing protocol pattern)Architecture
The approach reuses the existing
JSONAAPlug.Resource.Attributeprotocol without changing its signature. The distinction between a simple attribute and a composed one is driven entirely by thetype: :composeddeclaration in@derive— the normalizer reads this at runtime and changes how it routes the call:Compile-time validation raises explicit errors if
composed_ofis missing or ifserialize: false/deserialize: falseare used together withtype: :composed(which would be contradictory).recase (camelize, dasherize, underscore) is applied to derived field names just like regular attributes, so the JSON API wire format is consistent regardless of case configuration.
What does NOT change
serialize: false,deserialize: false, or a customdefimplare completely unaffected.JSONAAPlug.Resource.Attributeprotocol signature is unchanged.