packages feed

openapi-hs-4.1.0: test/Data/OpenApiSpec.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.OpenApiSpec where

import Control.Lens
import Data.Aeson
import Data.Aeson.QQ.Simple
import Data.HashMap.Strict (HashMap)
import Data.HashSet.InsOrd qualified as InsOrdHS
import Data.OpenApi
import Data.Text (Text)
import Prelude.Compat
import SpecCommon
import Test.Hspec hiding (example)
import Prelude ()

spec :: Spec
spec = do
  describe "License Object" $ licenseExample <=> licenseExampleJSON
  describe "Contact Object" $ contactExample <=> contactExampleJSON
  describe "Info Object" $ infoExample <=> infoExampleJSON
  describe "Operation Object" $ operationExample <=> operationExampleJSON
  describe "Schema Object" $ do
    context "Primitive Sample" $ schemaPrimitiveExample <=> schemaPrimitiveExampleJSON
    context "Simple Model" $ schemaSimpleModelExample <=> schemaSimpleModelExampleJSON
    context "Model with Map/Dictionary Properties" $ schemaModelDictExample <=> schemaModelDictExampleJSON
    context "Model with Arbitrary Properties" $ schemaAdditionalExample <=> schemaAdditionalExampleJSON
    context "Model with Example" $ schemaWithExampleExample <=> schemaWithExampleExampleJSON
  describe "Definitions Object" $ definitionsExample <=> definitionsExampleJSON
  describe "Parameters Definition Object" $ paramsDefinitionExample <=> paramsDefinitionExampleJSON
  describe "Responses Definition Object" $ responsesDefinitionExample <=> responsesDefinitionExampleJSON
  describe "Status Code Range Responses" $ statusRangeResponsesExample <=> statusRangeResponsesExampleJSON
  describe "Responses with default, exact, range and $ref" $ responsesMixedExample <=> responsesMixedExampleJSON
  describe "Issue #1: range status code parses"
    $ it "decodes an operation whose responses include a 4XX range"
    $ do
      let js =
            [aesonQQ|
{
  "operationId": "test_endpoint",
  "responses": {
    "200": { "description": "200 response" },
    "429": { "description": "too many requests" },
    "4XX": { "description": "client error" }
  }
}
|]
      case fromJSON js :: Result Operation of
        Success op ->
          (op ^. responses . at (StatusRange R4XX))
            `shouldBe` Just (Inline (mempty & description .~ "client error"))
        Error e -> expectationFailure ("expected successful parse, got: " <> e)
  describe "Responses key parsing" $ do
    it "rejects a lowercase range key"
      $ (fromJSON [aesonQQ| { "4xx": { "description": "x" } } |] :: Result Responses)
      `shouldSatisfy` isError
    it "rejects an out-of-range class key"
      $ (fromJSON [aesonQQ| { "6XX": { "description": "x" } } |] :: Result Responses)
      `shouldSatisfy` isError
    it "accepts every valid range class"
      $ ( fromJSON
            [aesonQQ| { "1XX": {"description":"a"}, "5XX": {"description":"b"} } |] ::
            Result Responses
        )
      `shouldSatisfy` isSuccess
  describe "Security Definitions Object" $ securityDefinitionsExample <=> securityDefinitionsExampleJSON
  describe "OAuth2 Security Definitions with merged Scope" $ oAuth2SecurityDefinitionsExample <=> oAuth2SecurityDefinitionsExampleJSON
  describe "OAuth2 Security Definitions with empty Scope" $ oAuth2SecurityDefinitionsEmptyExample <=> oAuth2SecurityDefinitionsEmptyExampleJSON
  describe "Composition Schema Example" $ compositionSchemaExample <=> compositionSchemaExampleJSON
  describe "Swagger Object" $ do
    context "Example with no paths" $ do
      emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
      it "fails to parse a spec with a wrong Openapi spec version" $ do
        (fromJSON wrongVersionExampleJSON :: Result OpenApi) `shouldBe` Error "The provided version 3.0.4 is out of the allowed range >=3.1.0 && <=3.1.1"
    context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
    context "PetStore Example" $ do
      it "decodes successfully" $ do
        fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: OpenApi) -> True; _ -> False)
      it "roundtrips: fmap toJSON . fromJSON" $ do
        (toJSON :: OpenApi -> Value) <$> fromJSON petstoreExampleJSON `shouldBe` Success petstoreExampleJSON
    context "Security schemes" $ do
      it "merged correctly" $ do
        let merged = oAuth2SecurityDefinitionsReadOpenApi <> oAuth2SecurityDefinitionsWriteOpenApi <> oAuth2SecurityDefinitionsEmptyOpenApi
        merged `shouldBe` oAuth2SecurityDefinitionsOpenApi

main :: IO ()
main = hspec spec

isError :: Result a -> Bool
isError (Error _) = True
isError _ = False

isSuccess :: Result a -> Bool
isSuccess (Success _) = True
isSuccess _ = False

-- =======================================================================
-- Info object
-- =======================================================================

infoExample :: Info
infoExample =
  mempty
    & title .~ "Swagger Sample App"
    & description ?~ "This is a sample server Petstore server."
    & termsOfService ?~ "http://swagger.io/terms/"
    & contact ?~ contactExample
    & license ?~ licenseExample
    & version .~ "1.0.1"

infoExampleJSON :: Value
infoExampleJSON =
  [aesonQQ|
{
  "title": "Swagger Sample App",
  "description": "This is a sample server Petstore server.",
  "termsOfService": "http://swagger.io/terms/",
  "contact": {
    "name": "API Support",
    "url": "http://www.swagger.io/support",
    "email": "support@swagger.io"
  },
  "license": {
    "name": "Apache 2.0",
    "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
  },
  "version": "1.0.1"
}
|]

-- =======================================================================
-- Contact object
-- =======================================================================

contactExample :: Contact
contactExample =
  mempty
    & name ?~ "API Support"
    & url ?~ URL "http://www.swagger.io/support"
    & email ?~ "support@swagger.io"

contactExampleJSON :: Value
contactExampleJSON =
  [aesonQQ|
{
  "name": "API Support",
  "url": "http://www.swagger.io/support",
  "email": "support@swagger.io"
}
|]

-- =======================================================================
-- License object
-- =======================================================================

licenseExample :: License
licenseExample =
  "Apache 2.0"
    & url ?~ URL "http://www.apache.org/licenses/LICENSE-2.0.html"

licenseExampleJSON :: Value
licenseExampleJSON =
  [aesonQQ|
{
  "name": "Apache 2.0",
  "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
|]

-- =======================================================================
-- Operation object
-- =======================================================================

operationExample :: Operation
operationExample =
  mempty
    & tags .~ InsOrdHS.fromList ["pet"]
    & summary ?~ "Updates a pet in the store with form data"
    & description ?~ ""
    & operationId ?~ "updatePetWithForm"
    & parameters
      .~ [ Inline
             ( mempty
                 & name .~ "petId"
                 & description ?~ "ID of pet that needs to be updated"
                 & required ?~ True
                 & in_ .~ ParamPath
                 & schema ?~ Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString)
             )
         ]
    & requestBody
      ?~ Inline
        ( mempty
            & content . at "application/x-www-form-urlencoded"
              ?~ ( mempty
                     & schema
                       ?~ ( Inline
                              ( mempty
                                  & properties . at "petId"
                                    ?~ Inline
                                      ( mempty
                                          & description ?~ "Updated name of the pet"
                                          & type_ ?~ OpenApiTypeSingle OpenApiString
                                      )
                                  & properties . at "status"
                                    ?~ Inline
                                      ( mempty
                                          & description ?~ "Updated status of the pet"
                                          & type_ ?~ OpenApiTypeSingle OpenApiString
                                      )
                              )
                          )
                 )
        )
    & at 200 ?~ "Pet updated."
    & at 405 ?~ "Invalid input"
    & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]

operationExampleJSON :: Value
operationExampleJSON =
  [aesonQQ|
{
  "tags": [
    "pet"
  ],
  "summary": "Updates a pet in the store with form data",
  "description": "",
  "operationId": "updatePetWithForm",
  "parameters": [
    {
      "required": true,
      "schema": {
        "type": "string"
      },
      "in": "path",
      "name": "petId",
      "description": "ID of pet that needs to be updated"
    }
  ],
  "requestBody": {
    "content": {
      "application/x-www-form-urlencoded": {
        "schema": {
          "properties": {
            "petId": {
              "type": "string",
              "description": "Updated name of the pet"
            },
            "status": {
              "type": "string",
              "description": "Updated status of the pet"
            }
          }
        }
      }
    }
  },
  "responses": {
    "200": {
      "description": "Pet updated."
    },
    "405": {
      "description": "Invalid input"
    }
  },
  "security": [
    {
      "petstore_auth": [
        "write:pets",
        "read:pets"
      ]
    }
  ]
}
|]

-- =======================================================================
-- Schema object
-- =======================================================================

schemaPrimitiveExample :: Schema
schemaPrimitiveExample =
  mempty
    & type_ ?~ OpenApiTypeSingle OpenApiString
    & format ?~ "email"

schemaPrimitiveExampleJSON :: Value
schemaPrimitiveExampleJSON =
  [aesonQQ|
{
    "type": "string",
    "format": "email"
}
|]

schemaSimpleModelExample :: Schema
schemaSimpleModelExample =
  mempty
    & type_ ?~ OpenApiTypeSingle OpenApiObject
    & required .~ ["name"]
    & properties
      .~ [ ("name", Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString)),
           ("address", Ref (Reference "Address")),
           ( "age",
             Inline $ mempty
               & minimum_ ?~ 0
               & type_ ?~ OpenApiTypeSingle OpenApiInteger
               & format ?~ "int32"
           )
         ]

schemaSimpleModelExampleJSON :: Value
schemaSimpleModelExampleJSON =
  [aesonQQ|
{  "required": [    "name"  ],
  "properties": {
    "name": {
      "type": "string"
    },
    "address": {
      "$ref": "#/components/schemas/Address"
    },
    "age": {
      "format": "int32",
      "minimum": 0,
      "type": "integer"
    }
  },
  "type": "object"
}
|]

schemaModelDictExample :: Schema
schemaModelDictExample =
  mempty
    & type_ ?~ OpenApiTypeSingle OpenApiObject
    & additionalProperties ?~ AdditionalPropertiesSchema (Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString))

schemaModelDictExampleJSON :: Value
schemaModelDictExampleJSON =
  [aesonQQ|
{
  "type": "object",
  "additionalProperties": {
    "type": "string"
  }
}
|]

schemaAdditionalExample :: Schema
schemaAdditionalExample =
  mempty
    & type_ ?~ OpenApiTypeSingle OpenApiObject
    & additionalProperties ?~ AdditionalPropertiesAllowed True

schemaAdditionalExampleJSON :: Value
schemaAdditionalExampleJSON =
  [aesonQQ|
{
  "type": "object",
  "additionalProperties": true
}
|]

schemaWithExampleExample :: Schema
schemaWithExampleExample =
  mempty
    & type_ ?~ OpenApiTypeSingle OpenApiObject
    & properties
      .~ [ ( "id",
             Inline $ mempty
               & type_ ?~ OpenApiTypeSingle OpenApiInteger
               & format ?~ "int64"
           ),
           ( "name",
             Inline $ mempty
               & type_ ?~ OpenApiTypeSingle OpenApiString
           )
         ]
    & required .~ ["name"]
    & example
      ?~ [aesonQQ|
    {
      "name": "Puma",
      "id": 1
    }
  |]

schemaWithExampleExampleJSON :: Value
schemaWithExampleExampleJSON =
  [aesonQQ|
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "format": "int64"
    },
    "name": {
      "type": "string"
    }
  },
  "required": [
    "name"
  ],
  "example": {
    "name": "Puma",
    "id": 1
  }
}
|]

-- =======================================================================
-- Definitions object
-- =======================================================================

definitionsExample :: HashMap Text Schema
definitionsExample =
  [ ( "Category",
      mempty
        & type_ ?~ OpenApiTypeSingle OpenApiObject
        & properties
          .~ [ ( "id",
                 Inline $ mempty
                   & type_ ?~ OpenApiTypeSingle OpenApiInteger
                   & format ?~ "int64"
               ),
               ("name", Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString))
             ]
    ),
    ( "Tag",
      mempty
        & type_ ?~ OpenApiTypeSingle OpenApiObject
        & properties
          .~ [ ( "id",
                 Inline $ mempty
                   & type_ ?~ OpenApiTypeSingle OpenApiInteger
                   & format ?~ "int64"
               ),
               ("name", Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString))
             ]
    )
  ]

definitionsExampleJSON :: Value
definitionsExampleJSON =
  [aesonQQ|
{
  "Category": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer",
        "format": "int64"
      },
      "name": {
        "type": "string"
      }
    }
  },
  "Tag": {
    "type": "object",
    "properties": {
      "id": {
        "type": "integer",
        "format": "int64"
      },
      "name": {
        "type": "string"
      }
    }
  }
}
|]

-- =======================================================================
-- Parameters Definition object
-- =======================================================================

paramsDefinitionExample :: HashMap Text Param
paramsDefinitionExample =
  [ ( "skipParam",
      mempty
        & name .~ "skip"
        & description ?~ "number of items to skip"
        & required ?~ True
        & in_ .~ ParamQuery
        & schema
          ?~ Inline
            ( mempty
                & type_ ?~ OpenApiTypeSingle OpenApiInteger
                & format ?~ "int32"
            )
    ),
    ( "limitParam",
      mempty
        & name .~ "limit"
        & description ?~ "max records to return"
        & required ?~ True
        & in_ .~ ParamQuery
        & schema
          ?~ Inline
            ( mempty
                & type_ ?~ OpenApiTypeSingle OpenApiInteger
                & format ?~ "int32"
            )
    )
  ]

paramsDefinitionExampleJSON :: Value
paramsDefinitionExampleJSON =
  [aesonQQ|
{
  "skipParam": {
    "name": "skip",
    "in": "query",
    "description": "number of items to skip",
    "required": true,
    "schema": {
      "type": "integer",
      "format": "int32"
    }
  },
  "limitParam": {
    "name": "limit",
    "in": "query",
    "description": "max records to return",
    "required": true,
    "schema": {
      "type": "integer",
      "format": "int32"
    }
  }
}
|]

-- =======================================================================
-- Responses Definition object
-- =======================================================================

responsesDefinitionExample :: HashMap Text Response
responsesDefinitionExample =
  [ ("NotFound", mempty & description .~ "Entity not found."),
    ("IllegalInput", mempty & description .~ "Illegal input for operation.")
  ]

responsesDefinitionExampleJSON :: Value
responsesDefinitionExampleJSON =
  [aesonQQ|
{
  "NotFound": {
    "description": "Entity not found."
  },
  "IllegalInput": {
    "description": "Illegal input for operation."
  }
}
|]

statusRangeResponsesExample :: Responses
statusRangeResponsesExample =
  mempty
    & at 200 ?~ Inline (mempty & description .~ "OK")
    & at (StatusRange R4XX) ?~ Inline (mempty & description .~ "Client error")

statusRangeResponsesExampleJSON :: Value
statusRangeResponsesExampleJSON =
  [aesonQQ|
{
  "200": { "description": "OK" },
  "4XX": { "description": "Client error" }
}
|]

responsesMixedExample :: Responses
responsesMixedExample =
  mempty
    & default_ ?~ Inline (mempty & description .~ "Unexpected error")
    & at 200 ?~ Inline (mempty & description .~ "OK")
    & at (StatusRange R4XX) ?~ Inline (mempty & description .~ "Client error")
    & at (StatusRange R5XX) ?~ Ref (Reference "ServerError")

responsesMixedExampleJSON :: Value
responsesMixedExampleJSON =
  [aesonQQ|
{
  "default": { "description": "Unexpected error" },
  "200": { "description": "OK" },
  "4XX": { "description": "Client error" },
  "5XX": { "$ref": "#/components/responses/ServerError" }
}
|]

-- =======================================================================
-- Security Definition object
-- =======================================================================

securityDefinitionsExample :: SecurityDefinitions
securityDefinitionsExample =
  SecurityDefinitions
    [ ( "api_key",
        SecurityScheme
          { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader),
            _securitySchemeDescription = Nothing
          }
      ),
      ( "petstore_auth",
        SecurityScheme
          { _securitySchemeType =
              SecuritySchemeOAuth2
                ( mempty
                    & implicit
                      ?~ OAuth2Flow
                        { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog",
                          _oAath2RefreshUrl = Nothing,
                          _oAuth2Scopes =
                            [ ("write:pets", "modify pets in your account"),
                              ("read:pets", "read your pets")
                            ]
                        }
                ),
            _securitySchemeDescription = Nothing
          }
      )
    ]

securityDefinitionsExampleJSON :: Value
securityDefinitionsExampleJSON =
  [aesonQQ|
{
  "api_key": {
    "in": "header",
    "name": "api_key",
    "type": "apiKey"
  },
  "petstore_auth": {
    "type": "oauth2",
    "flows": {
      "implicit": {
        "scopes": {
          "write:pets": "modify pets in your account",
          "read:pets": "read your pets"
        },
        "authorizationUrl": "http://swagger.io/api/oauth/dialog"
      }
    }
  }
}

|]

oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
oAuth2SecurityDefinitionsReadExample =
  SecurityDefinitions
    [ ( "petstore_auth",
        SecurityScheme
          { _securitySchemeType =
              SecuritySchemeOAuth2
                ( mempty
                    & implicit
                      ?~ OAuth2Flow
                        { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog",
                          _oAath2RefreshUrl = Nothing,
                          _oAuth2Scopes =
                            [("read:pets", "read your pets")]
                        }
                ),
            _securitySchemeDescription = Nothing
          }
      )
    ]

oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
oAuth2SecurityDefinitionsWriteExample =
  SecurityDefinitions
    [ ( "petstore_auth",
        SecurityScheme
          { _securitySchemeType =
              SecuritySchemeOAuth2
                ( mempty
                    & implicit
                      ?~ OAuth2Flow
                        { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog",
                          _oAath2RefreshUrl = Nothing,
                          _oAuth2Scopes =
                            [("write:pets", "modify pets in your account")]
                        }
                ),
            _securitySchemeDescription = Nothing
          }
      )
    ]

oAuth2SecurityDefinitionsEmptyExample :: SecurityDefinitions
oAuth2SecurityDefinitionsEmptyExample =
  SecurityDefinitions
    [ ( "petstore_auth",
        SecurityScheme
          { _securitySchemeType =
              SecuritySchemeOAuth2
                ( mempty
                    & implicit
                      ?~ OAuth2Flow
                        { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog",
                          _oAath2RefreshUrl = Nothing,
                          _oAuth2Scopes = []
                        }
                ),
            _securitySchemeDescription = Nothing
          }
      )
    ]

oAuth2SecurityDefinitionsExample :: SecurityDefinitions
oAuth2SecurityDefinitionsExample =
  oAuth2SecurityDefinitionsWriteExample
    <> oAuth2SecurityDefinitionsReadExample
    <> oAuth2SecurityDefinitionsEmptyExample

oAuth2SecurityDefinitionsExampleJSON :: Value
oAuth2SecurityDefinitionsExampleJSON =
  [aesonQQ|
{
  "petstore_auth": {
    "type": "oauth2",
    "flows": {
      "implicit": {
        "scopes": {
          "write:pets": "modify pets in your account",
          "read:pets": "read your pets"
        },
        "authorizationUrl": "http://swagger.io/api/oauth/dialog"
      }
    }
  }
}
|]

oAuth2SecurityDefinitionsEmptyExampleJSON :: Value
oAuth2SecurityDefinitionsEmptyExampleJSON =
  [aesonQQ|
{
  "petstore_auth": {
    "type": "oauth2",
    "flows": {
      "implicit": {
        "scopes": {},
        "authorizationUrl": "http://swagger.io/api/oauth/dialog"
      }
    }
  }
}
|]

oAuth2SecurityDefinitionsReadOpenApi :: OpenApi
oAuth2SecurityDefinitionsReadOpenApi =
  mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsReadExample

oAuth2SecurityDefinitionsWriteOpenApi :: OpenApi
oAuth2SecurityDefinitionsWriteOpenApi =
  mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsWriteExample

oAuth2SecurityDefinitionsEmptyOpenApi :: OpenApi
oAuth2SecurityDefinitionsEmptyOpenApi =
  mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsEmptyExample

oAuth2SecurityDefinitionsOpenApi :: OpenApi
oAuth2SecurityDefinitionsOpenApi =
  mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsExample

-- =======================================================================
-- Swagger object
-- =======================================================================

emptyPathsFieldExample :: OpenApi
emptyPathsFieldExample = mempty

wrongVersionExampleJSON :: Value
wrongVersionExampleJSON =
  [aesonQQ|
{
  "openapi": "3.0.4",
  "info": {"version": "", "title": ""},
  "paths": {},
  "components": {}
}
|]

emptyPathsFieldExampleJSON :: Value
emptyPathsFieldExampleJSON =
  [aesonQQ|
{
  "openapi": "3.1.0",
  "info": {"version": "", "title": ""},
  "paths": {},
  "components": {}
}
|]

swaggerExample :: OpenApi
swaggerExample =
  mempty
    -- & basePath ?~ "/"
    -- & schemes ?~ [Http]
    & info
      .~ ( mempty
             & version .~ "1.0"
             & title .~ "Todo API"
             & license ?~ "MIT"
             & license . _Just . url ?~ URL "http://mit.com"
             & description ?~ "This is an API that tests servant-swagger support for a Todo API"
         )
    & paths . at "/todo/{id}"
      ?~ ( mempty
             & get
               ?~ ( (mempty :: Operation)
                      & responses . at 200
                        ?~ Inline
                          ( mempty
                              & description .~ "OK"
                              & content . at "application/json"
                                ?~ ( mempty
                                       & schema
                                         ?~ Inline
                                           ( mempty
                                               & type_ ?~ OpenApiTypeSingle OpenApiObject
                                               & example
                                                 ?~ [aesonQQ|
                      {
                        "created": 100,
                        "description": "get milk"
                      } |]
                                               & description ?~ "This is some real Todo right here"
                                               & properties
                                                 .~ [ ( "created",
                                                        Inline $ mempty
                                                          & type_ ?~ OpenApiTypeSingle OpenApiInteger
                                                          & format ?~ "int32"
                                                      ),
                                                      ("description", Inline (mempty & type_ ?~ OpenApiTypeSingle OpenApiString))
                                                    ]
                                           )
                                   )
                          )
                      & parameters
                        .~ [ Inline $ mempty
                               & required ?~ True
                               & name .~ "id"
                               & description ?~ "TodoId param"
                               & in_ .~ ParamPath
                               & schema
                                 ?~ Inline
                                   ( mempty
                                       & type_ ?~ OpenApiTypeSingle OpenApiString
                                   )
                           ]
                      & tags .~ InsOrdHS.fromList ["todo"]
                  )
         )

swaggerExampleJSON :: Value
swaggerExampleJSON =
  [aesonQQ|
{
    "openapi": "3.1.0",
    "info": {
        "version": "1.0",
        "title": "Todo API",
        "license": {
            "url": "http://mit.com",
            "name": "MIT"
        },
        "description": "This is an API that tests servant-swagger support for a Todo API"
    },
    "paths": {
        "/todo/{id}": {
            "get": {
                "tags": [
                    "todo"
                ],
                "parameters": [
                    {
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "in": "path",
                        "name": "id",
                        "description": "TodoId param"
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "example": {
                                        "created": 100,
                                        "description": "get milk"
                                    },
                                    "type": "object",
                                    "description": "This is some real Todo right here",
                                    "properties": {
                                        "created": {
                                            "format": "int32",
                                            "type": "integer"
                                        },
                                        "description": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        },
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {}
}
|]

petstoreExampleJSON :: Value
petstoreExampleJSON =
  [aesonQQ|
{
  "openapi": "3.1.0",
  "info": {
    "version": "1.0.0",
    "title": "Swagger Petstore",
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "http://petstore.swagger.io/v1"
    }
  ],
  "paths": {
    "/pets": {
      "get": {
        "summary": "List all pets",
        "operationId": "listPets",
        "tags": [
          "pets"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "How many items to return at one time (max 100)",
            "required": false,
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A paged array of pets",
            "headers": {
              "x-next": {
                "description": "A link to the next page of responses",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "id",
                      "name"
                    ],
                    "properties": {
                      "id": {
                        "type": "integer",
                        "format": "int64"
                      },
                      "name": {
                        "type": "string"
                      },
                      "tag": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a pet",
        "operationId": "createPets",
        "tags": [
          "pets"
        ],
        "responses": {
          "201": {
            "description": "Null response"
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/pets/{petId}": {
      "get": {
        "summary": "Info for a specific pet",
        "operationId": "showPetById",
        "tags": [
          "pets"
        ],
        "parameters": [
          {
            "name": "petId",
            "in": "path",
            "required": true,
            "description": "The id of the pet to retrieve",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Expected response to a valid request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "name"
                  ],
                  "properties": {
                    "id": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "name": {
                      "type": "string"
                    },
                    "tag": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "code",
                    "message"
                  ],
                  "properties": {
                    "code": {
                      "type": "integer",
                      "format": "int32"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Pet": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "name": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          }
        }
      },
      "Pets": {
        "type": "array",
        "items": {
          "type": "object",
          "required": [
            "id",
            "name"
          ],
          "properties": {
            "id": {
              "type": "integer",
              "format": "int64"
            },
            "name": {
              "type": "string"
            },
            "tag": {
              "type": "string"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}
|]

compositionSchemaExample :: Schema
compositionSchemaExample =
  mempty
    & Data.OpenApi.allOf
      ?~ [ Ref (Reference "Other"),
           Inline
             ( mempty
                 & type_ ?~ OpenApiTypeSingle OpenApiObject
                 & properties
                   .~ [ ( "greet",
                          Inline $ mempty
                            & type_ ?~ OpenApiTypeSingle OpenApiString
                        )
                      ]
             )
         ]

compositionSchemaExampleJSON :: Value
compositionSchemaExampleJSON =
  [aesonQQ|
{
  "allOf": [
      {
         "$ref": "#/components/schemas/Other"
      },
      {
        "type": "object",
        "properties": {
          "greet": { "type": "string" }
        }
      }
  ]
}
|]