{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | __Example:__
--
-- >>> :set -XDerivingStrategies -XDerivingVia -XDeriveGeneric
--
-- >>> :{
-- data User = User
-- { userFirstName :: String,
-- userAge :: Maybe Integer
-- }
-- deriving stock (Generic)
-- deriving (FromJSON, ToJSON, ToSchema)
-- via CustomJSON
-- '[FieldLabelModifier '[StripPrefix "user", CamelToSnake], RejectUnknownFields]
-- User
-- showYaml = Data.ByteString.Char8.putStr . Data.Yaml.encode
-- :}
--
-- >>> showYaml $ toSchema (Proxy :: Proxy User)
-- type: object
-- properties:
-- first_name:
-- type: string
-- age:
-- type: integer
-- required:
-- - first_name
-- additionalProperties: false
module Deriving.OpenApi
( CustomOpenApi,
DatatypeNameModifier,
ToSchema,
module Deriving.Aeson,
)
where
import Control.Lens
import qualified Data.Aeson.Types as A
import Data.OpenApi
import Data.OpenApi.Internal.ParamSchema
import Data.OpenApi.Internal.Schema
import Data.Proxy
import Data.Text (Text)
import qualified Data.Text as T
import Data.Typeable
import Deriving.Aeson
import GHC.Generics
import GHC.TypeLits
data Description (desc :: Symbol) = Description
instance (AesonOptions xs) => AesonOptions (Description f ': xs) where
aesonOptions = aesonOptions @xs
instance KnownSymbol t => OpenApiOptionModifier (Description t) where
openApiSchemaModifier = schema . description <>~ Just (toTextLine @t)
toTextLine :: forall s. KnownSymbol s => Text
toTextLine = "\n\n" <> T.pack (symbolVal (Proxy @s))
-- $setup
-- >>> import qualified Data.Yaml
-- >>> import qualified Data.ByteString.Char8
type CustomOpenApi = CustomJSON
instance
(OpenApiOptionModifier xs, GToSchema (Rep x), Generic x, Typeable x, Typeable xs, Typeable k) =>
ToSchema (CustomJSON (xs :: k) x)
where
declareNamedSchema Proxy =
openApiSchemaModifier @xs
<$> genericDeclareNamedSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
instance
(OpenApiOptionModifier xs, GToParamSchema (Rep x), Generic x) =>
ToParamSchema (CustomJSON (xs :: k) x)
where
toParamSchema Proxy =
genericToParamSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
class OpenApiOptionModifier x where
openApiOptionsModifier :: SchemaOptions -> SchemaOptions
openApiOptionsModifier = id
openApiSchemaModifier :: NamedSchema -> NamedSchema
openApiSchemaModifier = id
data DatatypeNameModifier t
instance (StringModifier f) => OpenApiOptionModifier (DatatypeNameModifier f) where
openApiOptionsModifier o = o {datatypeNameModifier = getStringModifier @f}
instance (AesonOptions xs) => AesonOptions (DatatypeNameModifier f ': xs) where
aesonOptions = aesonOptions @xs
-- deriving-aeson-based instances
instance OpenApiOptionModifier UnwrapUnaryRecords where
openApiOptionsModifier o = o {unwrapUnaryRecords = True}
instance OpenApiOptionModifier OmitNothingFields
#if MIN_VERSION_deriving_aeson(0,2,4)
instance OpenApiOptionModifier RejectUnknownFields where
openApiSchemaModifier = schema . additionalProperties .~ Just (AdditionalPropertiesAllowed False)
#endif
instance StringModifier f => OpenApiOptionModifier (FieldLabelModifier f) where
openApiOptionsModifier o = o {fieldLabelModifier = getStringModifier @f}
instance StringModifier f => OpenApiOptionModifier (ConstructorTagModifier f) where
openApiOptionsModifier o = o {constructorTagModifier = getStringModifier @f}
instance
TypeError ('Text "deriving-openapi3 does not currently the `TagSingleConstructors` modifier.") =>
OpenApiOptionModifier TagSingleConstructors
instance OpenApiOptionModifier NoAllNullaryToStringTag where
openApiOptionsModifier o = o {allNullaryToStringTag = False}
instance (KnownSymbol t, KnownSymbol c) => OpenApiOptionModifier (SumTaggedObject t c) where
openApiOptionsModifier o = o {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
instance OpenApiOptionModifier SumUntaggedValue where
openApiOptionsModifier o = o {sumEncoding = A.UntaggedValue}
instance OpenApiOptionModifier SumObjectWithSingleField where
openApiOptionsModifier o = o {sumEncoding = A.ObjectWithSingleField}
instance OpenApiOptionModifier SumTwoElemArray where
openApiOptionsModifier o = o {sumEncoding = A.TwoElemArray}
instance OpenApiOptionModifier '[]
instance (OpenApiOptionModifier x, OpenApiOptionModifier xs) => OpenApiOptionModifier (x ': xs) where
openApiOptionsModifier = openApiOptionsModifier @xs . openApiOptionsModifier @x
openApiSchemaModifier = openApiSchemaModifier @xs . openApiSchemaModifier @x