packages feed

openapi3 3.2.2 → 3.2.3

raw patch · 12 files changed

+158/−50 lines, 12 filesdep −networkdep ~aesondep ~basedep ~base-compat-batteries

Dependencies removed: network

Dependency ranges changed: aeson, base, base-compat-batteries, hspec, mtl, template-haskell, transformers

Files

CHANGELOG.md view
@@ -1,6 +1,15 @@ Unreleased ---------- +3.2.3+-----++- Fix generation of `allOf` / `oneOf` schemas when variants are not objects [#49](https://github.com/biocad/openapi3/pull/49).+- Support parsing OpenAPI 3.0.1, 3.0.2, 3.0.3 versions [#68](https://github.com/biocad/openapi3/pull/68).+- Support GHC-9.4 [#61](https://github.com/biocad/openapi3/pull/61).+- Output `scopes` in schema even if empty [#65](https://github.com/biocad/openapi3/pull/65).+- Fix `mtl-2.3` compatibility [#60](https://github.com/biocad/openapi3/pull/60).+ 3.2.2 ----- 
openapi3.cabal view
@@ -1,6 +1,6 @@ cabal-version:       >=1.10 name:                openapi3-version:             3.2.2+version:             3.2.3  synopsis:            OpenAPI 3.0 data model category:            Web, Swagger, OpenApi@@ -61,21 +61,21 @@    -- GHC boot libraries   build-depends:-      base             >=4.11.1.0  && <4.17+      base             >=4.11.1.0  && <4.18     , bytestring       >=0.10.8.2  && <0.12     , containers       >=0.5.11.0  && <0.7-    , template-haskell >=2.13.0.0  && <2.19+    , template-haskell >=2.13.0.0  && <2.20     , time             >=1.8.0.2   && <1.14-    , transformers     >=0.5.5.0   && <0.6+    , transformers     >=0.5.5.0   && <0.7    build-depends:-      mtl              >=2.2.2   && <2.3+      mtl              >=2.2.2   && <2.4     , text             >=1.2.3.1 && <2.1    -- other dependencies   build-depends:       base-compat-batteries     >=0.11.1   && <0.13-    , aeson                     >=1.4.2.0  && <1.6 || >=2.0.1.0 && < 2.1+    , aeson                     >=1.4.2.0  && <1.6 || >=2.0.1.0 && < 2.2     , aeson-pretty              >=0.8.7    && <0.9     -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint     , cookie                    >=0.4.3    && <0.5@@ -83,14 +83,13 @@     , hashable                  >=1.2.7.0  && <1.5     , http-media                >=0.8.0.0  && <0.9     , insert-ordered-containers >=0.2.3    && <0.3-    , lens                      >=4.16.1   && <5.2-    , network                   >=2.6.3.5  && <3.2+    , lens                      >=4.16.1   && <5.3     , optics-core               >=0.2      && <0.5     , optics-th                 >=0.2      && <0.5     , scientific                >=0.3.6.2  && <0.4     , unordered-containers      >=0.2.9.0  && <0.3     , uuid-types                >=1.0.3    && <1.1-    , vector                    >=0.12.0.1 && <0.13+    , vector                    >=0.12.0.1 && <0.14     , QuickCheck                >=2.10.1   && <2.15    default-language:    Haskell2010@@ -122,14 +121,14 @@    -- test-suite only dependencies   build-depends:-      hspec                >=2.5.5   && <2.10+      hspec                >=2.5.5   && <2.11     , HUnit                >=1.6.0.0 && <1.7     , quickcheck-instances >=0.3.19  && <0.14     , utf8-string          >=1.0.1.1 && <1.1    -- https://github.com/haskell/cabal/issues/3708   build-tool-depends:-    hspec-discover:hspec-discover >=2.5.5 && <2.10+    hspec-discover:hspec-discover >=2.5.5 && <2.11    other-modules:     SpecCommon@@ -148,6 +147,7 @@   hs-source-dirs:   test   main-is:          doctests.hs   type:             exitcode-stdio-1.0+  build-depends:    base, openapi3  executable example   hs-source-dirs:   examples
src/Data/OpenApi.hs view
@@ -137,10 +137,13 @@ -- >>> import Data.Proxy -- >>> import GHC.Generics -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import Data.OpenApi.Internal+-- >>> import Data.OpenApi.Internal.Schema+-- >>> import qualified Data.HashMap.Strict.InsOrd as IOHM -- >>> import Data.OpenApi.Internal.Utils+-- >>> import Data.OpenApi.Lens -- >>> :set -XDeriveGeneric -- >>> :set -XOverloadedStrings--- >>> :set -XOverloadedLists -- >>> :set -fno-warn-missing-methods  -- $howto@@ -211,9 +214,9 @@ -- -- >>> :{ -- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)---   & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ]+--   & components . schemas .~ IOHM.fromList [ ("User", mempty & type_ ?~ OpenApiString) ] --   & paths .~---     [ ("/user", mempty & get ?~ (mempty+--     IOHM.fromList [ ("/user", mempty & get ?~ (mempty --         & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User"))) --         & at 404 ?~ "User info not found")) ] -- :}@@ -409,8 +412,7 @@ --             ], --             "type": "object" --         }---     ],---     "type": "object"+--     ] -- }  -- $manipulation
src/Data/OpenApi/Declare.hs view
@@ -16,7 +16,6 @@  import Control.Monad import Control.Monad.Cont (ContT)-import Control.Monad.List (ListT) import Control.Monad.Reader (ReaderT) import Control.Monad.Trans import Control.Monad.Trans.Except (ExceptT)
src/Data/OpenApi/Internal.hs view
@@ -43,7 +43,6 @@ import           GHC.Generics          (Generic) import           Network.HTTP.Media    (MediaType, mainType, parameters, parseAccept, subType, (//),                                         (/:))-import           Network.Socket        (HostName, PortNumber) import           Text.Read             (readMaybe)  import           Data.HashMap.Strict.InsOrd (InsOrdHashMap)@@ -56,6 +55,9 @@                                          sopSwaggerGenericToJSON, sopSwaggerGenericToJSONWithOpts) import Data.OpenApi.Internal.Utils import Generics.SOP.TH                  (deriveGeneric)+import Data.Version+import Control.Monad (unless)+import Text.ParserCombinators.ReadP (readP_to_S)  -- $setup -- >>> :seti -XDataKinds@@ -99,8 +101,19 @@      -- | Additional external documentation.   , _openApiExternalDocs :: Maybe ExternalDocs++  , -- | The spec of OpenApi this spec adheres to. Must be between 'lowerOpenApiSpecVersion' and 'upperOpenApiSpecVersion'+    _openApiOpenapi :: OpenApiSpecVersion   } deriving (Eq, Show, Generic, Data, Typeable) +-- | This is the lower version of the OpenApi Spec this library can parse or produce+lowerOpenApiSpecVersion :: Version+lowerOpenApiSpecVersion = makeVersion [3, 0, 0]++-- | This is the upper version of the OpenApi Spec this library can parse or produce+upperOpenApiSpecVersion :: Version+upperOpenApiSpecVersion = makeVersion [3, 0, 3]+ -- | The object provides metadata about the API. -- The metadata MAY be used by the clients if needed, -- and MAY be presented in editing or documentation generation tools for convenience.@@ -962,6 +975,8 @@   | AdditionalPropertiesSchema (Referenced Schema)   deriving (Eq, Show, Data, Typeable) +newtype OpenApiSpecVersion = OpenApiSpecVersion {getVersion :: Version} deriving (Eq, Show, Generic, Data, Typeable)+ ------------------------------------------------------------------------------- -- Generic instances -------------------------------------------------------------------------------@@ -984,11 +999,19 @@ deriveGeneric ''Example deriveGeneric ''Encoding deriveGeneric ''Link+deriveGeneric ''OpenApiSpecVersion  -- ======================================================================= -- Monoid instances -- ======================================================================= +instance Semigroup OpenApiSpecVersion where+  (<>) (OpenApiSpecVersion a) (OpenApiSpecVersion b) = OpenApiSpecVersion $ max a b +  +instance Monoid OpenApiSpecVersion where+  mempty = OpenApiSpecVersion (makeVersion [3,0,0])+  mappend = (<>)+   instance Semigroup OpenApi where   (<>) = genericMappend instance Monoid OpenApi where@@ -1126,6 +1149,7 @@ instance SwaggerMonoid Operation instance (Eq a, Hashable a) => SwaggerMonoid (InsOrdHashSet a) instance SwaggerMonoid SecurityDefinitions+instance SwaggerMonoid OpenApiSpecVersion  instance SwaggerMonoid MimeList deriving instance SwaggerMonoid URL@@ -1258,6 +1282,9 @@ -- Manual ToJSON instances -- ======================================================================= +instance ToJSON OpenApiSpecVersion where +  toJSON (OpenApiSpecVersion v)= toJSON . showVersion $ v+ instance ToJSON MediaType where   toJSON = toJSON . show   toEncoding = toEncoding . show@@ -1266,7 +1293,10 @@   toJSONKey = JSON.toJSONKeyText (Text.pack . show)  instance (Eq p, ToJSON p, AesonDefaultValue p) => ToJSON (OAuth2Flow p) where-  toJSON = sopSwaggerGenericToJSON+  toJSON a = sopSwaggerGenericToJSON a &+    if InsOrdHashMap.null (_oAuth2Scopes a)+    then (<+> object ["scopes" .= object []])+    else id   toEncoding = sopSwaggerGenericToEncoding  instance ToJSON OAuth2Flows where@@ -1422,6 +1452,22 @@ -- Manual FromJSON instances -- ======================================================================= +instance FromJSON OpenApiSpecVersion where+  parseJSON = withText "OpenApiSpecVersion" $ \str ->+            let validatedVersion :: Either String Version+                validatedVersion = do+                  parsedVersion <- readVersion str +                  unless ((parsedVersion >= lowerOpenApiSpecVersion) && (parsedVersion <= upperOpenApiSpecVersion)) $+                     Left ("The provided version " <> showVersion parsedVersion <> " is out of the allowed range >=" <> showVersion lowerOpenApiSpecVersion <> " && <=" <> showVersion upperOpenApiSpecVersion)+                  return parsedVersion+             in +              either fail (return . OpenApiSpecVersion) validatedVersion+    where+    readVersion :: Text -> Either String Version+    readVersion v = case readP_to_S parseVersion (Text.unpack v) of +      [] -> Left $ "Failed to parse as a version string " <> Text.unpack v+      solutions -> Right (fst . last $ solutions)+ instance FromJSON MediaType where   parseJSON = withText "MediaType" $ \str ->     maybe (fail $ "Invalid media type literal " <> Text.unpack str) pure $ parseAccept $ encodeUtf8 str@@ -1591,8 +1637,10 @@   swaggerAesonOptions _ = mkSwaggerAesonOptions "securityScheme" & saoSubObject ?~ "type" instance HasSwaggerAesonOptions Schema where   swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"+instance HasSwaggerAesonOptions OpenApiSpecVersion where+  swaggerAesonOptions _ = mkSwaggerAesonOptions "openapi" instance HasSwaggerAesonOptions OpenApi where-  swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]+  swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" instance HasSwaggerAesonOptions Example where   swaggerAesonOptions _ = mkSwaggerAesonOptions "example" instance HasSwaggerAesonOptions Encoding where@@ -1601,6 +1649,9 @@ instance HasSwaggerAesonOptions Link where   swaggerAesonOptions _ = mkSwaggerAesonOptions "link" +instance AesonDefaultValue Version where +  defaultValue = Just (makeVersion [3,0,0])+instance AesonDefaultValue OpenApiSpecVersion instance AesonDefaultValue Server instance AesonDefaultValue Components instance AesonDefaultValue OAuth2ImplicitFlow
src/Data/OpenApi/Internal/Schema.hs view
@@ -28,7 +28,7 @@ import Data.Data.Lens (template)  import Control.Monad-import Control.Monad.Writer+import Control.Monad.Writer hiding (First, Last) import Data.Aeson (Object (..), SumEncoding (..), ToJSON (..), ToJSONKey (..),                    ToJSONKeyFunction (..), Value (..)) import Data.Char@@ -50,6 +50,7 @@ import Data.Scientific (Scientific) import Data.Fixed (Fixed, HasResolution, Pico) import Data.Set (Set)+import Data.Semigroup import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Time@@ -92,6 +93,13 @@ rename :: Maybe T.Text -> NamedSchema -> NamedSchema rename name (NamedSchema _ schema) = NamedSchema name schema +-- $setup+-- >>> import Data.Aeson.Types (toJSONKeyText)+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import Data.OpenApi.Internal+-- >>> import Data.OpenApi.Internal.Utils (encodePretty)+-- >>> import Data.OpenApi.Lens (name, schema)+ -- | Convert a type into @'Schema'@. -- -- An example type and instance:@@ -1026,7 +1034,6 @@   | otherwise = do     (schemas, _) <- runWriterT declareSumSchema     return $ unnamed $ mempty-      & type_ ?~ OpenApiObject       & oneOf ?~ (snd <$> schemas)   where     declareSumSchema = gsumToSchema opts proxy@@ -1071,7 +1078,6 @@            -- In the remaining cases we combine "tag" object and "contents" object using allOf.           _ -> Inline $ mempty-            & type_ ?~ OpenApiObject             & allOf ?~ [Inline $ mempty               & type_ ?~ OpenApiObject               & required .~ (T.pack tagField : if isRecord then [] else [T.pack contentsField])
src/Data/OpenApi/Internal/Schema/Validation.hs view
@@ -59,6 +59,9 @@ import Data.OpenApi.Internal.Utils import Data.OpenApi.Lens +-- $setup+-- >>> import Data.OpenApi.Internal.Schema.Validation+ -- | Validate @'ToJSON'@ instance matches @'ToSchema'@ for a given value. -- This can be used with QuickCheck to ensure those instances are coherent: --
src/Data/OpenApi/Operation.hs view
@@ -55,12 +55,13 @@ -- >>> import Data.Proxy -- >>> import Data.Time -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import qualified Data.HashMap.Strict.InsOrd as IOHM -- >>> import Data.OpenApi.Internal.Utils  -- | Prepend path piece to all operations of the spec. -- Leading and trailing slashes are trimmed/added automatically. ----- >>> let api = (mempty :: OpenApi) & paths .~ [("/info", mempty)]+-- >>> let api = (mempty :: OpenApi) & paths .~ IOHM.fromList [("/info", mempty)] -- >>> BSL.putStrLn $ encodePretty $ prependPath "user/{user_id}" api ^. paths -- { --     "/user/{user_id}/info": {}@@ -83,8 +84,8 @@ -- by both path and method. -- -- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"--- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]--- >>> let sub = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]+-- >>> let api = (mempty :: OpenApi) & paths .~ IOHM.fromList [("/user", mempty & get ?~ ok & post ?~ ok)]+-- >>> let sub = (mempty :: OpenApi) & paths .~ IOHM.fromList [("/user", mempty & get ?~ mempty)] -- >>> BSL.putStrLn $ encodePretty api -- { --     "components": {},@@ -215,7 +216,7 @@ -- -- Example: ----- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]+-- >>> let api = (mempty :: OpenApi) & paths .~ IOHM.fromList [("/user", mempty & get ?~ mempty)] -- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day) -- >>> BSL.putStrLn $ encodePretty $ api & setResponse 200 res -- {
src/Data/OpenApi/Optics.hs view
@@ -18,14 +18,15 @@ -- >>> import Optics.Core -- >>> :set -XOverloadedLabels -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import qualified Data.HashMap.Strict.InsOrd as IOHM -- -- Example from the "Data.OpenApi" module using @optics@: -- -- >>> :{ -- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi)---   & #components % #schemas .~ [ ("User", mempty & #type ?~ OpenApiString) ]+--   & #components % #schemas .~ IOHM.fromList [ ("User", mempty & #type ?~ OpenApiString) ] --   & #paths .~---     [ ("/user", mempty & #get ?~ (mempty+--     IOHM.fromList [ ("/user", mempty & #get ?~ (mempty --         & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User"))) --         & at 404 ?~ "User info not found")) ] -- :}
test/Data/OpenApi/CommonTestTypes.hs view
@@ -312,8 +312,7 @@         }       }     }-  ],-  "type": "object"+  ] }  |]@@ -398,8 +397,7 @@         }       }     }-  ],-  "type": "object"+  ] } |] @@ -455,8 +453,7 @@         }       }     }-  ],-  "type": "object"+  ] } |] @@ -705,8 +702,7 @@         }       }     }-  ],-  "type": "object"+  ] } |] @@ -789,8 +785,7 @@         }       }     }-  ],-  "type": "object"+  ] } |] @@ -929,8 +924,7 @@           "required": ["tag", "contents"],           "type": "object"         }-      ],-      "type": "object"+      ]     },     "Noun": {       "properties": {@@ -969,8 +963,7 @@           "required": ["tag", "contents"],           "type": "object"         }-      ],-      "type": "object"+      ]     },     "Omitted": {       "properties": {
test/Data/OpenApiSpec.hs view
@@ -36,9 +36,13 @@   describe "Responses Definition Object" $ responsesDefinitionExample <=> responsesDefinitionExampleJSON   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" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON+    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.0.0 && <=3.0.3"     context "Todo Example" $ swaggerExample <=> swaggerExampleJSON     context "PetStore Example" $ do       it "decodes successfully" $ do@@ -47,7 +51,7 @@         (toJSON :: OpenApi -> Value) <$> fromJSON petstoreExampleJSON `shouldBe` Success petstoreExampleJSON     context "Security schemes" $ do       it "merged correctly" $ do-        let merged = oAuth2SecurityDefinitionsReadOpenApi <> oAuth2SecurityDefinitionsWriteOpenApi+        let merged = oAuth2SecurityDefinitionsReadOpenApi <> oAuth2SecurityDefinitionsWriteOpenApi <> oAuth2SecurityDefinitionsEmptyOpenApi         merged `shouldBe` oAuth2SecurityDefinitionsOpenApi  main :: IO ()@@ -508,10 +512,22 @@       , _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+  oAuth2SecurityDefinitionsReadExample <>+  oAuth2SecurityDefinitionsEmptyExample  oAuth2SecurityDefinitionsExampleJSON :: Value oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|@@ -531,6 +547,21 @@ } |] +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@@ -539,6 +570,10 @@ oAuth2SecurityDefinitionsWriteOpenApi =   mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsWriteExample +oAuth2SecurityDefinitionsEmptyOpenApi :: OpenApi+oAuth2SecurityDefinitionsEmptyOpenApi =+  mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsEmptyExample+ oAuth2SecurityDefinitionsOpenApi :: OpenApi oAuth2SecurityDefinitionsOpenApi =   mempty & components . securitySchemes .~ oAuth2SecurityDefinitionsExample@@ -550,6 +585,16 @@ emptyPathsFieldExample :: OpenApi emptyPathsFieldExample = mempty +wrongVersionExampleJSON :: Value+wrongVersionExampleJSON = [aesonQQ|+{+  "openapi": "3.0.4",+  "info": {"version": "", "title": ""},+  "paths": {},+  "components": {}+}+|]+ emptyPathsFieldExampleJSON :: Value emptyPathsFieldExampleJSON = [aesonQQ| {@@ -663,7 +708,7 @@ petstoreExampleJSON :: Value petstoreExampleJSON = [aesonQQ| {-  "openapi": "3.0.0",+  "openapi": "3.0.3",   "info": {     "version": "1.0.0",     "title": "Swagger Petstore",@@ -933,7 +978,6 @@  compositionSchemaExample :: Schema compositionSchemaExample = mempty-  & type_ ?~ OpenApiObject   & Data.OpenApi.allOf ?~ [       Ref (Reference "Other")     , Inline (mempty@@ -946,7 +990,6 @@ compositionSchemaExampleJSON :: Value compositionSchemaExampleJSON = [aesonQQ| {-  "type": "object",   "allOf": [       {          "$ref": "#/components/schemas/Other"
test/doctests.hs view
@@ -9,4 +9,4 @@     traverse_ putStrLn args     doctest args   where-    args = flags ++ pkgs ++ module_sources+    args = flags ++ ["-package-db=/nix/store/pl15d7r0mhfpygks2s8cal3jsjivwcl8-ghc-shell-for-openapi3-ghc-8.10.7-env/lib/ghc-8.10.7/package.conf.d"] ++ pkgs ++ module_sources