{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
-- | Functionality to Generate Haskell Code out of an OpenAPI definition File
module OpenAPI.Generate where
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import Data.Text (Text)
import qualified Data.Text as T
import Data.Yaml
import Language.Haskell.TH
import Language.Haskell.TH.PprLib hiding ((<>))
import qualified OpenAPI.Common as OC
import qualified OpenAPI.Generate.Doc as Doc
import qualified OpenAPI.Generate.Model as Model
import qualified OpenAPI.Generate.ModelDependencies as Dep
import qualified OpenAPI.Generate.Monad as OAM
import qualified OpenAPI.Generate.Operation as Operation
import qualified OpenAPI.Generate.SecurityScheme as SecurityScheme
import qualified OpenAPI.Generate.Types as OAT
import System.Exit
-- | Decodes an OpenAPI File
decodeOpenApi :: String -> IO OAT.OpenApiSpecification
decodeOpenApi fileName = do
res <- decodeFileEither fileName
case res of
Left exc -> die $ "Could not parse OpenAPI specification '" <> fileName <> "': " <> show exc
Right o -> pure o
-- | Defines all the operations as functions and the common imports
defineOperations :: String -> OAT.OpenApiSpecification -> OAM.Generator (Q [Dep.ModuleDefinition])
defineOperations moduleName =
OAM.nested "paths"
. fmap
( fmap
concat
. sequence
)
. mapM (uncurry $ Operation.defineOperationsForPath moduleName)
. Map.toList
. OAT.paths
-- | Defines the @defaultURL@ and the @defaultConfiguration@ containing this URL.
defineConfigurationInformation :: String -> OAT.OpenApiSpecification -> Q Doc
defineConfigurationInformation moduleName spec =
let servers' = (OAT.servers :: OAT.OpenApiSpecification -> [OAT.ServerObject]) spec
defaultURL = getServerURL servers'
defaultURLName = mkName "defaultURL"
getServerURL = maybe "/" (OAT.url :: OAT.ServerObject -> Text) . Maybe.listToMaybe
in Doc.addConfigurationModuleHeader moduleName
. vcat
<$> sequence
[ pure $
Doc.generateHaddockComment
[ "The default url specified by the OpenAPI specification",
"",
"@" <> defaultURL <> "@"
],
ppr
<$> [d|$(varP defaultURLName) = T.pack $(litE $ stringL $ T.unpack defaultURL)|],
pure $ Doc.generateHaddockComment ["The default configuration containing the 'defaultURL' and no authorization"],
ppr <$> [d|$(varP $ mkName "defaultConfiguration") = OC.Configuration $(varE defaultURLName) OC.AnonymousSecurityScheme|]
]
-- | Defines all models in the components.schemas section of the 'OAT.OpenApiSpecification'
defineModels :: String -> OAT.OpenApiSpecification -> OAM.Generator (Q [Dep.ModuleDefinition])
defineModels moduleName spec =
let schemaDefinitions = Map.toList $ OAT.schemas $ OAT.components spec
in OAM.nested "schemas" $ do
models <- mapM (uncurry Model.defineModelForSchema) schemaDefinitions
pure $ Dep.getModelModulesFromModelsWithDependencies moduleName models
-- | Defines all supported security schemes from the 'OAT.OpenApiSpecification'.
defineSecuritySchemes :: String -> OAT.OpenApiSpecification -> OAM.Generator (Q Doc)
defineSecuritySchemes moduleName =
OAM.nested "components"
. fmap (fmap $ Doc.addSecuritySchemesModuleHeader moduleName)
. SecurityScheme.defineSupportedSecuritySchemes (T.pack moduleName)
. Maybe.mapMaybe
( \(name', scheme') -> case scheme' of
OAT.Concrete s -> Just (name', s)
OAT.Reference _ -> Nothing
)
. Map.toList
. OAT.securitySchemes
. OAT.components