{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -- | Contains function to resolve references within the OpenAPI specification module OpenAPI.Generate.Reference ( ReferenceMap, ComponentReference (..), buildReferenceMap, getSchemaReference, getResponseReference, getParameterReference, getExampleReference, getRequestBodyReference, getHeaderReference, getSecuritySchemeReference, ) where import Control.Monad import qualified Data.Bifunctor as BF import qualified Data.Map as Map import qualified Data.Maybe as Maybe import Data.Text (Text) import GHC.Generics import qualified OpenAPI.Generate.Types as OAT import qualified OpenAPI.Generate.Types.Schema as OAS -- | Represents all types the 'ReferenceMap' can hold data ComponentReference = SchemaReference OAS.SchemaObject | ResponseReference OAT.ResponseObject | ParameterReference OAT.ParameterObject | ExampleReference OAT.ExampleObject | RequestBodyReference OAT.RequestBodyObject | HeaderReference OAT.HeaderObject | SecuritySchemeReference OAT.SecuritySchemeObject deriving (Show, Eq, Generic) -- | A lookup table for references within the OpenAPI specification type ReferenceMap = Map.Map Text ComponentReference -- | Creates a 'ReferenceMap' from an 'OAT.OpenApiSpecification' containing all elements within components. -- It does not capture possibly referenced locations anywhere else in the specification. buildReferenceMap :: OAT.OpenApiSpecification -> ReferenceMap buildReferenceMap = Map.fromList . ( \o -> buildReferencesForComponentType "schemas" SchemaReference (OAT.schemas o) <> buildReferencesForComponentType "responses" ResponseReference (OAT.responses (o :: OAT.ComponentsObject)) <> buildReferencesForComponentType "parameters" ParameterReference (OAT.parameters (o :: OAT.ComponentsObject)) <> buildReferencesForComponentType "examples" ExampleReference (OAT.examples (o :: OAT.ComponentsObject)) <> buildReferencesForComponentType "requestBodies" RequestBodyReference (OAT.requestBodies o) <> buildReferencesForComponentType "headers" HeaderReference (OAT.headers (o :: OAT.ComponentsObject)) <> buildReferencesForComponentType "securitySchemes" SecuritySchemeReference (OAT.securitySchemes o) ) . OAT.components -- | Maps the subtypes of components to the entries of the 'ReferenceMap' and filters references (the lookup table should only contain concrete values). buildReferencesForComponentType :: Text -> (a -> ComponentReference) -> Map.Map Text (OAT.Referencable a) -> [(Text, ComponentReference)] buildReferencesForComponentType componentName constructor = fmap (BF.first (("#/components/" <> componentName <> "/") <>)) . Maybe.mapMaybe (convertReferencableToReference constructor) . Map.toList convertReferencableToReference :: (a -> ComponentReference) -> (Text, OAT.Referencable a) -> Maybe (Text, ComponentReference) convertReferencableToReference constructor (name', OAT.Concrete object) = Just (name', constructor object) convertReferencableToReference _ (_, OAT.Reference _) = Nothing getReference :: Text -> ReferenceMap -> Maybe ComponentReference getReference = Map.lookup createReferenceLookup :: (ComponentReference -> Maybe a) -> Text -> ReferenceMap -> Maybe a createReferenceLookup conversionFn key = getReference key >=> conversionFn -- | Resolve a 'OAS.SchemaObject' reference from a 'ReferenceMap' getSchemaReference :: Text -> ReferenceMap -> Maybe OAS.SchemaObject getSchemaReference = createReferenceLookup $ \case SchemaReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.ResponseObject' reference from a 'ReferenceMap' getResponseReference :: Text -> ReferenceMap -> Maybe OAT.ResponseObject getResponseReference = createReferenceLookup $ \case ResponseReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.ParameterObject' reference from a 'ReferenceMap' getParameterReference :: Text -> ReferenceMap -> Maybe OAT.ParameterObject getParameterReference = createReferenceLookup $ \case ParameterReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.ExampleObject' reference from a 'ReferenceMap' getExampleReference :: Text -> ReferenceMap -> Maybe OAT.ExampleObject getExampleReference = createReferenceLookup $ \case ExampleReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.RequestBodyObject' reference from a 'ReferenceMap' getRequestBodyReference :: Text -> ReferenceMap -> Maybe OAT.RequestBodyObject getRequestBodyReference = createReferenceLookup $ \case RequestBodyReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.HeaderObject' reference from a 'ReferenceMap' getHeaderReference :: Text -> ReferenceMap -> Maybe OAT.HeaderObject getHeaderReference = createReferenceLookup $ \case HeaderReference r -> Just r _ -> Nothing -- | Resolve a 'OAT.SecuritySchemeObject' reference from a 'ReferenceMap' getSecuritySchemeReference :: Text -> ReferenceMap -> Maybe OAT.SecuritySchemeObject getSecuritySchemeReference = createReferenceLookup $ \case SecuritySchemeReference r -> Just r _ -> Nothing