{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
-- | Functionality to Generate Haskell Code out of an OpenAPI definition File
module OpenAPI.Generate.Main where
import Control.Monad
import qualified Data.Bifunctor as BF
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe
import qualified Data.Set as Set
import qualified Data.Text as T
import Language.Haskell.TH
import Language.Haskell.TH.PprLib hiding ((<>))
import qualified OpenAPI.Common as OC
import qualified OpenAPI.Generate.Doc as Doc
import OpenAPI.Generate.Internal.Unknown
import OpenAPI.Generate.Internal.Util
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.OptParse as OAO
import qualified OpenAPI.Generate.SecurityScheme as SecurityScheme
import qualified OpenAPI.Generate.Types as OAT
-- | Defines all the operations as functions and the common imports
defineOperations :: String -> OAT.OpenApiSpecification -> OAM.Generator (Q [Dep.ModuleDefinition], Dep.Models)
defineOperations moduleName specification =
let paths = Map.toList $ OAT.openApiSpecificationPaths specification
in OAM.nested "paths" $ do
warnAboutUnknownOperations paths
fmap
( BF.bimap
( fmap concat
. sequence
)
Set.unions
)
. mapAndUnzipM (uncurry $ Operation.defineOperationsForPath moduleName)
$ paths
-- | Defines the @defaultURL@ and the @defaultConfiguration@ containing this URL.
defineConfigurationInformation :: String -> OAT.OpenApiSpecification -> Q Doc
defineConfigurationInformation moduleName spec =
let defaultURL = getServerURL $ OAT.openApiSpecificationServers spec
defaultURLName = mkName "defaultURL"
getServerURL = maybe "/" OAT.serverObjectUrl . Maybe.listToMaybe
defaultApplicationNameVarName = mkName "defaultApplicationName"
defaultApplicationName = OAT.infoObjectTitle $ OAT.openApiSpecificationInfo spec
in Doc.addConfigurationModuleHeader moduleName
. vcat
<$> sequence
[ pure $
Doc.generateHaddockComment
[ "The default url specified by the OpenAPI specification",
"",
"@" <> defaultURL <> "@"
],
ppr
<$> [d|$(varP defaultURLName) = T.pack $(stringE $ T.unpack defaultURL)|],
pure $
Doc.generateHaddockComment
[ "The default application name used in the @User-Agent@ header which is based on the @info.title@ field of the specification",
"",
"@" <> defaultApplicationName <> "@"
],
ppr
<$> [d|$(varP defaultApplicationNameVarName) = T.pack $(stringE $ T.unpack defaultApplicationName)|],
pure $ Doc.generateHaddockComment ["The default configuration containing the 'defaultURL' and no authorization"],
ppr <$> [d|$(varP $ mkName "defaultConfiguration") = OC.Configuration $(varE defaultURLName) OC.anonymousSecurityScheme True $(varE defaultApplicationNameVarName)|]
]
-- | Defines all models in the components.schemas section of the 'OAT.OpenApiSpecification'
defineModels :: String -> OAT.OpenApiSpecification -> Dep.Models -> OAM.Generator (Q [Dep.ModuleDefinition])
defineModels moduleName spec operationDependencies =
let schemaDefinitions = Map.toList $ OAT.componentsObjectSchemas $ OAT.openApiSpecificationComponents spec
in OAM.nested "components" $
OAM.nested "schemas" $ do
warnAboutUnknownWhiteListedOrOpaqueSchemas schemaDefinitions
models <- mapM (uncurry Model.defineModelForSchema) schemaDefinitions
whiteListedSchemas <- OAM.getSetting OAO.settingWhiteListedSchemas
outputAllSchemas <- OAM.getSetting OAO.settingOutputAllSchemas
let dependencies = Set.union operationDependencies $ Set.fromList $ fmap transformToModuleName whiteListedSchemas
pure $ Dep.getModelModulesFromModelsWithDependencies moduleName dependencies outputAllSchemas 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.componentsObjectSecuritySchemes
. OAT.openApiSpecificationComponents