Search code examples
dhall

How to encode union on the subset of fields using Dhall?


I'm trying to use Dhall to generate AWS Cloudformation and the first thing I'm trying to encode is AWS::ApiGatewayV2::Api. Which has following the json spec:

{
  "Type" : "AWS::ApiGatewayV2::Api",
  "Properties" : {
    "ApiKeySelectionExpression" : String,
    "BasePath" : String,
    "Body" : Json,
    "BodyS3Location" : BodyS3Location,
    "CorsConfiguration" : Cors,
    "CredentialsArn" : String,
    "Description" : String,
    "DisableSchemaValidation" : Boolean,
    "FailOnWarnings" : Boolean,
    "Name" : String,
    "ProtocolType" : String,
    "RouteKey" : String,
    "RouteSelectionExpression" : String,
    "Tags" : Json,
    "Target" : String,
    "Version" : String
  }
}

The specification has multiple fields but two of them form a union: BodyS3Location and Body. Mean that either one of them should be present. I'm aware of the support for dynamic records but apparently only works for objects with single record. What would be the recommended way to encode this behavior?


Solution

  • This might not generalize well, but for this specific example you can do this:

    let JSON = https://prelude.dhall-lang.org/v12.0.0/JSON/package.dhall
    
    -- These are just placeholders for whatever the real types are
    let BodyS3Location = {}
    
    let Cors = {}
    
    let Shared =
          { ApiKeySelectionExpression : Text
          , BasePath : Text
          , CorsConfiguration : Cors
          , CredentialsArn : Text
          , Description : Text
          , DisableSchemaValidation : Bool
          , FailOnWarnings : Bool
          , Name : Text
          , ProtocolType : Text
          , RouteKey : Text
          , RouteSelectionExpression : Text
          , Tags : JSON.Type
          , Target : Text
          , Version : Text
          }
    
    in  < A : Shared //\\ { BodyS3Location : BodyS3Location }
        | B : Shared //\\ { Body : JSON.Type }
        >