FsCodec
FsCodec copied to clipboard
feat: Contract types validation
In a team environment, where the way event contracts are composed is often a matter of debate, it can be useful to have a way to validate that types that will be mapped to JSON adhere to conventions.
It should be pretty possible to, a la AutoFixture.Idioms
use reflection to find all DUs that implement TypeShape.UnionEncoder.IUnionContract
, and then walk the type with TypeShape counting anomalies such as
- using
FSharpList
without a converter (usearray
or[] option
) - using tuples anywhere in a union contract (a converter, e.g. based on a JsonIsomorphism should be permitted, but we definitely don't want random Newtonsoft/STJ representations and/or runtime exceptions)
- Using
DateTime
(DateTimeOffset
is hands down better) - Using Enums (use nullary unions / TypeSafeEnums in preference)
- using SCDUs without a converter (should probably be using UMX in the data contracts and only using SCDUs where warranted in models)
- using other unions without tagging the type and/or field with a converter (e.g. UnionConverter) (vs ending up e.g. triggering Newtonsofts built-in encoding)
This would enable a team for write a single unit test saying something like:
let [<Fact>] ``All contract types use simple tagged types`` () =
let checker = FsCodec.NewtonsoftJson.TypeChecker(allowMaps=true) // or any other exceptions to the search
let anoms =
TypesImplementing<TypeShape.UnionEncoder.IUnionContract>(typeof<Domain.SampleType>.Assembly)
|> Seq.collect checker.EnumViolations
|> List.ofSeq
test <@ [] = anoms @>
Reasons to have this in FsCodec vs anywhere else
- it's already using TypeShape
- we can port it to do the same thing for SystemTextJson
Yes, one could build a Roslyn Analyzer and/or Rider or Ionide checks too!
cc @fnipo can you think of any other stuff that needs to be added that can be done generically without running the serializer
related: (autoUnion = true)
can be used (with some cons) to mitigate some of the concerns/risks this feature idea would cover (although this idea would remove the risk of a transition from one encoding to another without there being a clear change in the code)