Principled code generation from OpenAPI specifications. https://guardrail.dev/
blast-hardcheese on master
Update jackson-annotations, jac… Merge pull request #1677 from s… (compare)
blast-hardcheese on master
Update jackson-datatype-jsr310 … Merge pull request #1678 from s… (compare)
blast-hardcheese on master
Update jackson-module-scala to … Merge pull request #1679 from s… (compare)
ObjectMapper
from the route class
@Inject
one into the class...)
required=true,nullable=false
properties when a default is present. like, i could see the argument that a client could actually leave that property out entirely, because the server should know about the default, and automatically fall back (so these sorts of properties automatically get 'demoted' to required=false,nullable=false
). but maybe you want to think of it as more of a "UX default": the server will barf if you don't include it, and the default there is mainly to make it so the user of the client doesn't have to explicitly fill in the property (because the client can do it for them).
At the risk of making the wrong gut decision here, but required=true,nullable=false
with a default should have the default injected by the server before the user sees it, regardless of presence in the actual wire encoding. If that means translating that into required=false,nullable=false
then so be it.
The expectation would then be that removing the default
from the spec would cause the generated code to change and a compiler error to be emitted.
i'm still struggling a bit, though. the more i think about it... well, take the most "flexible" case, required=false,nullable=true
. that's our tri-state of absent, null, or present. if i think about reflecting the intent of the user of the client, i would say that leaving things as absent would mean the client is saying "i don't care", and the server should assume the default value. if the client explicitly sends null
, then i would think the client is saying "really, i mean no value here", and the server should not substitute the default.
if we accept that as reasonable -- maybe we won't, but let's assume we will, then what does that mean for the other two non-trivial cases?
required=false,nullable=false
with a default still makes sense. if the client leaves the field empty, that still sounds like the "i don't care" case, and the server should substitute the default value.
but does required=true,nullable=true
with a default even make sense here? if the client says null
, then the server should probably see that and be like "ok, client says and means no value"? maybe this is purely a "UX default" again: the client exposes API to say "explicitly send a null", or "explicitly send a value", and if the user chooses neither of those, then the client sends the default value?
but maybe thinking about this with "intent" in mind isn't the right approach; maybe this is all about wire format. maybe someone doesn't like leaving fields out, and so "whatever": null
really does mean (to the server) "substitute the default if you see null".
required=true,nullable=false
case, that the server should accept a request where the field is missing (or maybe eve null), but substitute the default when calling the handler? that seems a little weird to me, because to me the required/nullable knobs to me talk about the wire format, and required=true,nullable=false
means that a request where that field is missing or null is an invalid request
"whatever": null
example wherein you say "substitute the default if you see null"
Presence
class to say .withFoo(Presence.Default)
or something
required
into required: true
or something
"whatever": null
on the server when required=false
to end up giving them the default value in the handler.
i think the thing i'm struggling with is if we should be interpreting the required, nullable, and default as instructions about the wire format, or about the UX of the server or client, or in some cases both. i think it's pretty safe to assume that required and nullable are first and foremost about the wire format, though they can affect the UX of the client and server. whereas default might be entirely about the UX of the client and server.... but we still have to make those UX decisions in the context of the wire format.
like if someone says required=false,nullable=true,default=foobar
, are they saying "i just want to have a really permissible wire format, where you can leave the field out or set it to null, and no one cares, but the UX should never see a null/absent and should always have foobar
substituted when there's no value or no field"?
or are they saying "absent field means they don't care and we should substitute the default, and null
means they specifically want no value and we should pass that through"?
but then i try to think about the actual use cases. since CRUD is a big use case, i think... ok, on create
, you probably mean the first thing. i can't think of a non-bonkers API where creating a resource has a field where you can either ask for the default, ask for null, or set a value. that just seems weird to me. but maybe someone wants to do that? then i think about update
, and my feeling there is that a default doesn't ever really make sense. in that case absent has to mean "don't modify this field", null has to mean "set this field to null", and a value has to mean "set this value". and if someone does set a default value for a field in an update
, i would say they are doing it wrong, because that doesn't make sense.
so maybe we can just say... ok, if you provide a default, we'll of course obey required/nullable when it comes to the wire format, but from the perspective of the server (when handling a request) or client (when inspecting a response), there is no such thing as absent or null. that property always has a value, and if we don't see one on the wire, that value is the default value. and yes, that means that if someone puts a default on their update
operation, things might be broken, but that's kinda on them for doing something logically nonsensical.
outside of CRUD, are there places where someone might want that "absent=default, null=null, value=value" interpretation of what comes over the wire? maybe? can we come up with something concrete, or is this just navel-gazing? the problem is that if we decide that there's nothing concrete here, and don't allow that interpretation, and then someone comes along and says they need it, we can't support it without either breaking existing behavior (nope) or adding another command-line switch to enable it. but on the flip side, if we do add support for this case from the start, at least people who don't care about this distinction (and just want "absent=default, null=default, value=value") can still implement that in their services without too much trouble (but yes, to be fair, the UX there ends up being slightly sub-optimal).
to make sure we're talking about the same possibilities, we've got...
Is that correct?