@@ -9,8 +9,9 @@ module Web.Template.Servant.Swagger
99
1010import Control.Lens (_Just , (%~) , (&) , (?~) )
1111import Data.Aeson (FromJSON , ToJSON , Value (.. ))
12- import Data.OpenApi (ToSchema (.. ), defaultSchemaOptions , description , enum_ ,
13- genericDeclareNamedSchema , schema )
12+ import Data.OpenApi (NamedSchema (.. ), Referenced (.. ), ToSchema (.. ),
13+ declareSchemaRef , defaultSchemaOptions , description , enum_ ,
14+ genericDeclareNamedSchema , oneOf , schema )
1415import Data.OpenApi.Internal.Schema (GToSchema , rename )
1516import Data.Override (Override )
1617import Data.Override.Aeson ()
@@ -47,8 +48,24 @@ newtype WithDescription (descr :: Symbol) a
4748
4849instance (Typeable a , KnownSymbol descr , ToSchema a ) => ToSchema (WithDescription descr a ) where
4950 declareNamedSchema _ = do
50- sch <- declareNamedSchema @ a Proxy
51- return $ sch & schema . description ?~ pack (symbolVal @ descr Proxy )
51+ -- Different fields may use the same type with different descriptions.
52+ -- To support this we use a hack: generate schema for a type, and
53+ -- wrap it for specific field with 'oneOf' with exactly one option, adding
54+ -- concrete description.
55+ --
56+ -- Schema for a field with description should be unnamed, to prevent it
57+ -- from reusage in other fields.
58+ --
59+ -- OpenAPI 3.1 will have a mechanism for this without hacks, but we use 3.0.
60+ sch <- declareSchemaRef @ a Proxy
61+ case sch of
62+ -- If schema for a field is something simple like "type: string",
63+ -- add description directly to it. It won't be shared with other fields.
64+ Inline sub -> return $ NamedSchema Nothing $ sub
65+ & description ?~ pack (symbolVal @ descr Proxy )
66+ Ref ref -> return $ NamedSchema Nothing $ mempty
67+ & description ?~ pack (symbolVal @ descr Proxy )
68+ & oneOf ?~ [Ref ref]
5269
5370-- | Describe possible enumeration values for a 'Text' field.
5471--
0 commit comments