I have been generating some encoders for calling AWS services recently, and having to deal with Maybe
fields.
Sometimes it can be important to distinguish between a field being null
versus simply missing. For example an HTTP UPATE might interpret a null
value as meaning, set this thing to null, but a missing value as leave this thing alone. A PATCH might be more correct in this scenario, and I am sure none of us design APIs that are so flaky, but out in the wild you see this kind of thing all too often.
As you can see the generated code (at the bottom) is a bit noisy. I can think of ways of adding some helper functions to tidy it up. Something along these lines:
module Encode.ExtraExtra exposing (..)
{-| Any field that is `Nothing` is output as `null`. -}
objectWithNulls : List (Maybe ( String, Value )) -> Value
{-| Any field that is `Nothing` is not output at all. -}
objectWithMissing : List (Maybe ( String, Value )) -> Value
field : (String, Value) -> Maybe (String, Value)
optionalField : (String, Maybe a) -> (a -> Value) -> Maybe (String, Value)
Or I could use some other type than Maybe
, perhaps Field
to try and make it more explicit that these are fields of an object and can only be used in that context.
In my experience so far, the AWS APIs have been stable under “null” or missing. But it would still be nice to be able to toggle between them with objectWithNulls
and objectWithMissing
for other APIs, or if I do run into awkward ones on AWS.
Is there a package that already does this? I thought perhaps minibill/elm-codec
had the ability to choose between nulls and missing?
===
encoder val =
[ Maybe.map (\fld -> ( "VpcConfig", fld |> Codec.encoder vpcConfigCodec )) val.vpcConfig
, Maybe.map (\fld -> ( "TracingConfig", fld |> Codec.encoder tracingConfigCodec )) val.tracingConfig
, Maybe.map (\fld -> ( "Timeout", fld |> Codec.encoder timeoutCodec )) val.timeout
, Maybe.map (\fld -> ( "Tags", fld |> Codec.encoder tagsCodec )) val.tags
, Just ( "Runtime", val.runtime |> Codec.encoder runtimeCodec )
, Just ( "Role", val.role |> Codec.encoder roleArnCodec )
, Maybe.map (\fld -> ( "Publish", fld |> Codec.encoder booleanCodec )) val.publish
, Maybe.map (\fld -> ( "MemorySize", fld |> Codec.encoder memorySizeCodec )) val.memorySize
, Maybe.map (\fld -> ( "Layers", fld |> Codec.encoder layerListCodec )) val.layers
, Maybe.map (\fld -> ( "KMSKeyArn", fld |> Codec.encoder kmskeyArnCodec )) val.kmskeyArn
, Just ( "Handler", val.handler |> Codec.encoder handlerCodec )
, Just ( "FunctionName", val.functionName |> Codec.encoder functionNameCodec )
, Maybe.map (\fld -> ( "Environment", fld |> Codec.encoder environmentCodec )) val.environment
, Maybe.map (\fld -> ( "Description", fld |> Codec.encoder descriptionCodec )) val.description
, Maybe.map
(\fld -> ( "DeadLetterConfig", fld |> Codec.encoder deadLetterConfigCodec ))
val.deadLetterConfig
, Just ( "Code", val.code |> Codec.encoder functionCodeCodec )
]
|> Maybe.Extra.values
|> Json.Encode.object