@@ -1,18 +1,45 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
6+-- >>> :set -XDerivingStrategies -XDerivingVia -XDeriveGeneric
10+-- { userFirstName :: String,
11+-- userAge :: Maybe Integer
13+-- deriving stock (Generic)
14+-- deriving (FromJSON, ToJSON, ToSchema)
16+-- '[FieldLabelModifier '[StripPrefix "user", CamelToSnake], RejectUnknownFields]
18+-- showYaml = Data.ByteString.Char8.putStr . Data.Yaml.encode
21+-- >>> showYaml $ toSchema (Proxy :: Proxy User)
30+-- additionalProperties: false
3module Deriving.OpenApi
31module Deriving.OpenApi
4 ( CustomOpenApi,
32 ( CustomOpenApi,
5 DatatypeNameModifier,
33 DatatypeNameModifier,
7 module Deriving.Aeson,
35 module Deriving.Aeson,
12import Control.Lens
39import Control.Lens
13import qualified Data.Aeson.Types as A
40import qualified Data.Aeson.Types as A
15import Data.OpenApi
41import Data.OpenApi
42+import Data.OpenApi.Internal.ParamSchema
16import Data.OpenApi.Internal.Schema
43import Data.OpenApi.Internal.Schema
17import Data.Proxy
44import Data.Proxy
18import Data.Text (Text)
45import Data.Text (Text)
@@ -21,82 +48,92 @@ import Data.Typeable
21import Deriving.Aeson
48import Deriving.Aeson
22import GHC.Generics
49import GHC.Generics
23import GHC.TypeLits
50import GHC.TypeLits
26−type CustomOpenApi = CustomJSON
52+data Description (desc :: Symbol) = Description
28−instance (OpenApiOptions xs, GToSchema (Rep x), Generic x, Typeable x, Typeable xs) => ToSchema (CustomJSON xs x) where
29− declareNamedSchema Proxy =
30− openApiSchemaModifier @xs <$> genericDeclareNamedSchema (openApiOptions @xs) (Proxy @x)
32−data DatatypeNameModifier t
34−instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (DatatypeNameModifier f ': xs) where
35− openApiOptions = (openApiOptions @xs) {datatypeNameModifier = getStringModifier @f}
37−instance (AesonOptions xs) => AesonOptions (DatatypeNameModifier f ': xs) where
38− aesonOptions = aesonOptions @xs
40instance (AesonOptions xs) => AesonOptions (Description f ': xs) where
54instance (AesonOptions xs) => AesonOptions (Description f ': xs) where
41 aesonOptions = aesonOptions @xs
55 aesonOptions = aesonOptions @xs
43−instance (OpenApiOptions xs, KnownSymbol t) => OpenApiOptions (Description t ': xs) where
57+instance KnownSymbol t => OpenApiOptionModifier (Description t) where
44− openApiOptions = openApiOptions @xs
45 openApiSchemaModifier = schema . description <>~ Just (toTextLine @t)
58 openApiSchemaModifier = schema . description <>~ Just (toTextLine @t)
47toTextLine :: forall s. KnownSymbol s => Text
60toTextLine :: forall s. KnownSymbol s => Text
48toTextLine = "\n\n" <> T.pack (symbolVal (Proxy @s))
61toTextLine = "\n\n" <> T.pack (symbolVal (Proxy @s))
50−type family (++) (x :: [k]) (y :: [k]) :: [k] where
63+-- $setup
51− (++) '[] ys = ys
64+-- >>> import qualified Data.Yaml
52− (++) (x ': xs) ys = xs ++ (x ': ys)
65+-- >>> import qualified Data.ByteString.Char8
54−type family Extending (c :: * -> *) (ee :: [*]) :: * -> * where
67+type CustomOpenApi = CustomJSON
55− Extending (CustomJSON a) ee = CustomJSON (a ++ ee)
57−-- deriving-aeson-based instances
69+instance
70+ (OpenApiOptionModifier xs, GToSchema (Rep x), Generic x, Typeable x, Typeable xs, Typeable k) =>
71+ ToSchema (CustomJSON (xs :: k) x)
73+ declareNamedSchema Proxy =
74+ openApiSchemaModifier @xs
75+ <$> genericDeclareNamedSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
59−class OpenApiOptions (xs :: [Type]) where
77+instance
60− openApiOptions :: SchemaOptions
78+ (OpenApiOptionModifier xs, GToParamSchema (Rep x), Generic x) =>
79+ ToParamSchema (CustomJSON (xs :: k) x)
81+ toParamSchema Proxy =
82+ genericToParamSchema (openApiOptionsModifier @xs defaultSchemaOptions) (Proxy @x)
84+class OpenApiOptionModifier x where
85+ openApiOptionsModifier :: SchemaOptions -> SchemaOptions
86+ openApiOptionsModifier = id
61 openApiSchemaModifier :: NamedSchema -> NamedSchema
87 openApiSchemaModifier :: NamedSchema -> NamedSchema
62 openApiSchemaModifier = id
88 openApiSchemaModifier = id
64−instance OpenApiOptions '[] where
90+data DatatypeNameModifier t
65− openApiOptions = defaultSchemaOptions
67−instance OpenApiOptions xs => OpenApiOptions (UnwrapUnaryRecords ': xs) where
92+instance (StringModifier f) => OpenApiOptionModifier (DatatypeNameModifier f) where
68− openApiOptions = (openApiOptions @xs) {unwrapUnaryRecords = True}
93+ openApiOptionsModifier o = o {datatypeNameModifier = getStringModifier @f}
70−instance OpenApiOptions xs => OpenApiOptions (OmitNothingFields ': xs) where
95+instance (AesonOptions xs) => AesonOptions (DatatypeNameModifier f ': xs) where
71− openApiOptions = openApiOptions @xs
96+ aesonOptions = aesonOptions @xs
73−instance OpenApiOptions xs => OpenApiOptions (RejectUnknownFields ': xs) where
98+-- deriving-aeson-based instances
74− openApiOptions = openApiOptions @xs
99+
100+instance OpenApiOptionModifier UnwrapUnaryRecords where
101+ openApiOptionsModifier o = o {unwrapUnaryRecords = True}
103+instance OpenApiOptionModifier OmitNothingFields
105+#if MIN_VERSION_deriving_aeson(0,2,4)
106+instance OpenApiOptionModifier RejectUnknownFields where
75 openApiSchemaModifier = schema . additionalProperties .~ Just (AdditionalPropertiesAllowed False)
107 openApiSchemaModifier = schema . additionalProperties .~ Just (AdditionalPropertiesAllowed False)
77−instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (FieldLabelModifier f ': xs) where
110+instance StringModifier f => OpenApiOptionModifier (FieldLabelModifier f) where
78− openApiOptions = (openApiOptions @xs) {fieldLabelModifier = getStringModifier @f}
111+ openApiOptionsModifier o = o {fieldLabelModifier = getStringModifier @f}
80−instance (StringModifier f, OpenApiOptions xs) => OpenApiOptions (ConstructorTagModifier f ': xs) where
113+instance StringModifier f => OpenApiOptionModifier (ConstructorTagModifier f) where
81− openApiOptions = (openApiOptions @xs) {constructorTagModifier = getStringModifier @f}
114+ openApiOptionsModifier o = o {constructorTagModifier = getStringModifier @f}
84− (OpenApiOptions xs, TypeError ('Text "openapi3-deriving does not currently the `TagSingleConstructors` modifier.")) =>
117+ TypeError ('Text "deriving-openapi3 does not currently the `TagSingleConstructors` modifier.") =>
85− OpenApiOptions (TagSingleConstructors ': xs)
118+ OpenApiOptionModifier TagSingleConstructors
87− openApiOptions = undefined
120+instance OpenApiOptionModifier NoAllNullaryToStringTag where
121+ openApiOptionsModifier o = o {allNullaryToStringTag = False}
123+instance (KnownSymbol t, KnownSymbol c) => OpenApiOptionModifier (SumTaggedObject t c) where
124+ openApiOptionsModifier o = o {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
89−instance OpenApiOptions xs => OpenApiOptions (NoAllNullaryToStringTag ': xs) where
126+instance OpenApiOptionModifier SumUntaggedValue where
90− openApiOptions = (openApiOptions @xs) {allNullaryToStringTag = False}
127+ openApiOptionsModifier o = o {sumEncoding = A.UntaggedValue}
92−instance (KnownSymbol t, KnownSymbol c, OpenApiOptions xs) => OpenApiOptions (SumTaggedObject t c ': xs) where
129+instance OpenApiOptionModifier SumObjectWithSingleField where
93− openApiOptions = (openApiOptions @xs) {sumEncoding = A.TaggedObject (symbolVal (Proxy @t)) (symbolVal (Proxy @c))}
130+ openApiOptionsModifier o = o {sumEncoding = A.ObjectWithSingleField}
95−instance (OpenApiOptions xs) => OpenApiOptions (SumUntaggedValue ': xs) where
132+instance OpenApiOptionModifier SumTwoElemArray where
96− openApiOptions = (openApiOptions @xs) {sumEncoding = A.UntaggedValue}
133+ openApiOptionsModifier o = o {sumEncoding = A.TwoElemArray}
98−instance (OpenApiOptions xs) => OpenApiOptions (SumObjectWithSingleField ': xs) where
135+instance OpenApiOptionModifier '[]
99− openApiOptions = (openApiOptions @xs) {sumEncoding = A.ObjectWithSingleField}
101−instance (OpenApiOptions xs) => OpenApiOptions (SumTwoElemArray ': xs) where
137+instance (OpenApiOptionModifier x, OpenApiOptionModifier xs) => OpenApiOptionModifier (x ': xs) where
102− openApiOptions = (openApiOptions @xs) {sumEncoding = A.TwoElemArray}
138+ openApiOptionsModifier = openApiOptionsModifier @xs . openApiOptionsModifier @x
139+ openApiSchemaModifier = openApiSchemaModifier @xs . openApiSchemaModifier @x