Closed hochgi closed 1 month ago
/bounty $250
/attempt #2962
with your implementation plan/claim #2962
in the PR body to claim the bountyThank you for contributing to zio/zio-http!
Add a bounty • Share on socials
Attempt | Started (GMT+0) | Solution |
---|---|---|
🟢 @hochgi | Jul 31, 2024, 6:49:43 PM | #3002 |
/attempt #2962
I'll give this a go. Going for alias via ref (the more "global" approach)
💡 @hochgi submitted a pull request that claims the bounty. You can visit your bounty board to reward.
🎉🎈 @hochgi has been awarded $250! 🎈🎊
Is your feature request related to a problem? Please describe.
In business logic code we want to use Newtype to make sure we don't mix up different fields of the same type. Generated code is great, but all primitives can still be mixed up. This is especially true when we don't have a "stable source", e.g. when an
openapi.yaml
spec is retrieved during build from a live service, which can change.consider a simple example like:
generated class would be:
and example code usage might be:
but a simple change in the order of fields in yaml, or a field rename may result in the generated class to look different, e.g:
which will cause our code to still compile, but be wrong:
code didn't change, but now "John" is the surname, and "Doe" is the first name.
Describe the solution you'd like
Ideally, we control the OpenAPI spec, and can define it "safer", in a way that enables generated code to use safe type aliases. e.g. by defining aliases directly, and using
ref
s , e.g:such definition can be picked up by zio-http-gen to render
FirstName
&LastName
as e.g. prelude Newtypes:Alternatively, we can consider some
x-codegen
annotations as described here. e.g:to achieve a similar goal.
Describe alternatives you've considered
What's currently possible, if we're being very meticulous, we can still avoid the issue of silent breakage if we keep a "manual" safe copy of the classes with safe Newtype aliases. We then can use
Schema.migrate
to help us transform all safe instances to the generated ones, and vice versa. While this works (if we are careful to always use named fields - thus code won't compile when changes like the one mentioned above are introduced), it is still inferior to an ideal solution when we generate safe code to begin with.Additional context
Using a made up annotation like
x-codegen-type-alias
would work, but is "local" to the component using it. If we have several components that "need" the same semantics over a primitive, it makes sense to make it "global". Relying on "same named type" might be ambiguous. Consider a type alias likeID
. It can mean a person ID number, or order ID, etc'… using aref
makes it clear different components use the SAME semantics of the field. Perhaps an annotation likex-codegen-type-alias
can be considered in the future for non-opaque type aliases?Perhaps the feature would be configurable. I think that by default primitives should generate Newtype aliases. The configuration can control whether to generate prelude Newtypes, regular scala 3 opaque types, opt-out aliasing altogether to render the primitive type directly, or anything else we might want to enable (e.g. 3rd party support for libs like neotype)