Skip to content

feat: Contract types validation #50

@bartelink

Description

@bartelink

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 (use array 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)
  • using anything that binds to a name Item* (e.g. unions with single item or tuple bodies)

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

  1. it's already using TypeShape
  2. 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!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions