Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validateJSON semantics with allOf seem incorrect #93

Open
pbrisbin opened this issue Feb 29, 2024 · 0 comments
Open

validateJSON semantics with allOf seem incorrect #93

pbrisbin opened this issue Feb 29, 2024 · 0 comments

Comments

@pbrisbin
Copy link

I have a Haskell type that is, roughly:

data WithMetadata a m = WithMetadata a m

instance (ToJSON a, ToJSON m) => ToJSON (WithMetadata a m) where
  toJSON (WithMeta a m) = unionObjects (toJSON a) (toJSON m)

unionObjects :: Value -> Value -> Value
unionObjects = undefined

Basically it just says that { foo: bar } `WithMetadata` { baz: bat } encodes like { foo: bar, baz: bat }

Writing the ToSchema for this was surprisingly pleasant,

instance (ToSchema a, ToSchema m) => ToSchema (a `WithMetadata` m) where
  declareNamedSchema _ = do
    aSchema <- declareSchemaRef (Proxy @a)
    mSchema <- declareSchemaRef (Proxy @m)

    pure $
      NamedSchema Nothing $
        mempty
          & type_ ?~ OpenApiObject
          & allOf ?~ [aSchema, mSchema]
Schema and expanded references
    {
        "items": {
            "allOf": [
                {
                    "$ref": "#/components/schemas/StandardSet" // this is the a
                },
                {
                    "properties": { // and this is the m
                        "administrativeAreas": {
                            "items": {
                                "$ref": "#/components/schemas/CountryAdministrativeArea"
                            },
                            "type": "array"
                        }
                    },
                    "required": [
                        "administrativeAreas"
                    ],
                    "type": "object"
                }
            ],
            "type": "object"
        },
        "type": "array"
    }
   {
        "CountryAdministrativeArea": {
            "properties": {
                "administrativeArea": {
                    "type": "string"
                },
                "countryCode": {
                    "$ref": "#/components/schemas/CountryCode"
                }
            },
            "required": [
                "countryCode"
            ],
            "type": "object"
        },
        "CountryCode": {
            "type": "string"
        },
        "StandardSet": {
            "properties": {
                "description": {
                    "type": "string"
                },
                "domainLabel": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                },
                "isLive": {
                    "type": "boolean"
                },
                "name": {
                    "type": "string"
                },
                "standardLabel": {
                    "type": "string"
                }
            },
            "required": [
                "id",
                "name",
                "description",
                "domainLabel",
                "standardLabel",
                "isLive"
            ],
            "type": "object"
        }
    }

I don't claim to know the semantics of allOf, but the documentation came out exactly as I wanted: it shows me an object with all of the properties of a and m:

[
  {
    "description": "string", // these fields are the ToJSON of a
    "domainLabel": "string",
    "id": "string",
    "isLive": true,
    "name": "string",
    "standardLabel": "string",
    "administrativeAreas": [ // and this field is the ToJSON of m
      {
        "administrativeArea": "string",
        "countryCode": "string"
      }
    ]
  }
]

As well as a combined schema, as if I had written it by hand:

But then I tried to use Data.OpenApi.validateJSON with this schema and a valid example, and I get a failure on every single field being unexpected:

  Errors:
    - property "administrativeAreas" is found in JSON value, but it is not mentioned in Swagger schema
    - property "description" is found in JSON value, but it is not mentioned in Swagger schema
    - property "domainLabel" is found in JSON value, but it is not mentioned in Swagger schema
    - property "id" is found in JSON value, but it is not mentioned in Swagger schema
    - property "isLive" is found in JSON value, but it is not mentioned in Swagger schema
    - property "name" is found in JSON value, but it is not mentioned in Swagger schema
    - property "standardLabel" is found in JSON value, but it is not mentioned in Swagger schema

I think this comes from the implementation for allOf:

    (view allOf -> Just variants) -> do
      -- Default semantics for Validation Monad will abort when at least one
      -- variant does not match.
      forM_ variants $ \var ->
        validateWithSchemaRef var val

What is probably happening is it is validating each of the allOf schemas individually, so any properties appear as extra when validating any one of the other schemas that don't specify them.

I think (again, just based on what the Example and Schema docs show), the intent of allOf (at least with type: object) would be to union them all into one schema, then validate with that.

The comment says "'Default' semantics" -- is there some way I can get the semantics I expected instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant