Skip to content

Commit bf91ba6

Browse files
authored
version 0.1.3.7: field descriptions in Swagger (#22)
1 parent 4d170ef commit bf91ba6

File tree

7 files changed

+130
-7
lines changed

7 files changed

+130
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.1.3.7] - 2021-06-10
10+
### Added
11+
- Add a way to describe fields in Swagger schemas.
12+
913
## [0.1.3.6] - 2021-06-09
1014
### Changed
1115
- Add default expiration time for OIDC discovery document and JWKS when provider does not set

app/ServantApp.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import Web.Template.Servant (OIDCAuth, OIDCConfig (..), Permit, SwaggerSchemaUI,
1717
swaggerSchemaUIServer)
1818
import Web.Template.Wai (defaultHandleLog, defaultHeaderCORS)
1919

20-
2120
type API = Version "1" :>
2221
( Summary "ping route" :> Description "Returns pong" :> "ping" :> Get '[PlainText] Text
2322
:<|> OIDCAuth :>

src/Web/Template/Servant.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module Web.Template.Servant
1010
, module Web.Template.Servant.API
1111
, module Web.Template.Servant.Auth
1212
, module Web.Template.Servant.Error
13+
, module Web.Template.Servant.Swagger
1314
) where
1415

1516
import Data.Proxy (Proxy (..))
@@ -26,6 +27,7 @@ import Web.Template.Servant.API
2627
import Web.Template.Servant.Aeson
2728
import Web.Template.Servant.Auth
2829
import Web.Template.Servant.Error
30+
import Web.Template.Servant.Swagger
2931

3032
runServantServer
3133
:: forall api

src/Web/Template/Servant/Aeson.hs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import Data.Aeson
55
import Data.Aeson.Casing
66
import Data.Functor ((<&>))
77
import Data.Proxy (Proxy (..))
8+
import Data.Text (pack)
89
import Data.Typeable (Typeable)
910
import GHC.Generics
11+
import Type.Reflection (typeRep)
1012

1113
import Data.OpenApi
1214
import Data.OpenApi.Internal.Schema
15+
import Data.Override (Override)
1316

1417
-- | This wrapper is intended to be used with @DerivingVia@ to make
1518
-- consistent 'ToJSON', 'FromJSON' and 'ToSchema' for some data type.
@@ -38,7 +41,18 @@ instance (Generic a, GToJSON Zero (Rep a), GToEncoding Zero (Rep a)) => ToJSON (
3841
instance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (CamelCaseAeson a) where
3942
parseJSON = fmap CamelCaseAeson . genericParseJSON prefixOptions
4043

41-
instance (Generic a, GToSchema (Rep a), Typeable a) => ToSchema (CamelCaseAeson a) where
44+
instance {-# OVERLAPS #-}
45+
( Typeable a
46+
, Typeable xs
47+
, Generic (Override a xs)
48+
, GToSchema (Rep (Override a xs))
49+
) => ToSchema (CamelCaseAeson (Override a xs)) where
50+
declareNamedSchema _ =
51+
-- Prevent "Override" from showing up in schema name.
52+
rename (Just $ pack $ show $ typeRep @a) <$>
53+
genericDeclareNamedSchema @(Override a xs) (fromAesonOptions prefixOptions) Proxy
54+
55+
instance {-# OVERLAPS #-} (Generic a, GToSchema (Rep a), Typeable a) => ToSchema (CamelCaseAeson a) where
4256
declareNamedSchema _ =
4357
genericDeclareNamedSchema @a (fromAesonOptions prefixOptions) Proxy
4458

src/Web/Template/Servant/Auth.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,12 @@ instance HasOpenApi api => HasOpenApi (CbdAuth :> api) where
114114
-- Usage:
115115
--
116116
-- > type API = OIDCAuth :> (....)
117-
-- Takes token from 'Authorization' header
118-
-- Handlers will get an 'UserId' argument
119-
-- Stores token and claims in vault
117+
--
118+
-- Takes token from 'Authorization' header.
119+
--
120+
-- Handlers will get an 'UserId' argument.
121+
--
122+
-- Stores token and claims in vault.
120123
data OIDCAuth
121124

122125
-- | Info needed for OIDC authorization & key cache
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{-# LANGUAGE DerivingStrategies #-}
2+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+
4+
{-# OPTIONS_GHC -Wno-orphans #-}
5+
module Web.Template.Servant.Swagger
6+
( WithDescription(..)
7+
, AsEnum(..)
8+
) where
9+
10+
import Control.Lens (_Just, (%~), (&), (?~))
11+
import Data.Aeson (FromJSON, ToJSON, Value (..))
12+
import Data.OpenApi (ToSchema (..), defaultSchemaOptions, description, enum_,
13+
genericDeclareNamedSchema, schema)
14+
import Data.OpenApi.Internal.Schema (GToSchema, rename)
15+
import Data.Override (Override)
16+
import Data.Override.Aeson ()
17+
import Data.Override.Internal (Overridden, Using)
18+
import Data.Proxy (Proxy (..))
19+
import Data.Text (Text, pack)
20+
import Data.Typeable (Typeable)
21+
import GHC.Generics (Generic (..))
22+
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
23+
import Type.Reflection (typeRep)
24+
25+
-- | Add a description to any field in a record.
26+
--
27+
-- Intended to be used with 'Data.Override.Override' from @generic-override@ package like this:
28+
--
29+
-- @
30+
-- data Foo =
31+
-- { fooSomeField :: Text
32+
-- , fooOtherField :: Int
33+
-- }
34+
-- deriving (Generic)
35+
-- deriving (ToJSON, FromJSON, ToSchema) via
36+
-- 'Web.Template.Servant.Aeson.CamelCaseAeson' ('Data.Override.Override' Foo
37+
-- '[ "fooSomeField" `'Data.Override.With'` 'WithDescription' "Some text describing a Foo"
38+
-- , "fooOtherField" `'Data.Override.With'` 'WithDescription' "A number of Foos"
39+
-- ])
40+
-- @
41+
--
42+
-- For techincal reasons, @CamelCaseAeson@ must be on the outer layer.
43+
newtype WithDescription (descr :: Symbol) a
44+
= WithDescription a
45+
deriving (Eq, Show)
46+
deriving newtype (ToJSON, FromJSON)
47+
48+
instance (Typeable a, KnownSymbol descr, ToSchema a) => ToSchema (WithDescription descr a) where
49+
declareNamedSchema _ = do
50+
sch <- declareNamedSchema @a Proxy
51+
return $ sch & schema . description ?~ pack (symbolVal @descr Proxy)
52+
53+
-- | Describe possible enumeration values for a 'Text' field.
54+
--
55+
-- To be used with 'Data.Override.Override':
56+
--
57+
-- @
58+
-- data Foo =
59+
-- { fooColor :: Text
60+
-- }
61+
-- deriving (Generic)
62+
-- deriving (ToJSON, FromJSON, ToSchema) via
63+
-- 'Web.Template.Servant.Aeson.CamelCaseAeson' ('Data.Override.Override' Foo
64+
-- '[ "fooColor" `'Data.Override.As`' 'AsEnum' '["red", "black", "white"]
65+
-- ])
66+
-- @
67+
newtype AsEnum (vals :: [Symbol])
68+
= AsEnum Text
69+
deriving (Eq, Show)
70+
deriving newtype (ToJSON, FromJSON)
71+
72+
instance ToSchema (AsEnum '[]) where
73+
declareNamedSchema _ = do
74+
sch <- declareNamedSchema @Text Proxy
75+
return $ sch & schema . enum_ ?~ []
76+
77+
instance (KnownSymbol val, Typeable vals, ToSchema (AsEnum vals)) => ToSchema (AsEnum (val ': vals)) where
78+
declareNamedSchema _ = do
79+
sch <- declareNamedSchema @(AsEnum vals) Proxy
80+
return $ sch & schema . enum_ . _Just %~ (String (pack $ symbolVal @val Proxy) :)
81+
82+
instance
83+
( Typeable a
84+
, Typeable xs
85+
, Generic (Override a xs)
86+
, GToSchema (Rep (Override a xs))
87+
) => ToSchema (Override a xs) where
88+
declareNamedSchema p =
89+
-- Prevent "Override" from showing up in schema name.
90+
rename (Just $ pack $ show $ typeRep @a) <$> genericDeclareNamedSchema defaultSchemaOptions p
91+
92+
instance
93+
( Typeable a
94+
, Typeable xs
95+
, Typeable ms
96+
, ToSchema (Using ms a xs)
97+
) => ToSchema (Overridden ms a xs) where
98+
declareNamedSchema _ = declareNamedSchema @(Using ms a xs) Proxy

web-template.cabal

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: web-template
2-
version: 0.1.3.6
2+
version: 0.1.3.7
33
synopsis: Web template
44
description:
55
Web template includes:
@@ -29,6 +29,7 @@ library
2929
, Web.Template.Servant.Auth
3030
, Web.Template.Servant.Error
3131
, Web.Template.Servant.Error.Instance
32+
, Web.Template.Servant.Swagger
3233
other-modules: Web.Template.Except
3334
, Web.Template.Server
3435
, Web.Template.Types
@@ -44,10 +45,12 @@ library
4445
, cookie
4546
, data-default
4647
, fast-logger
47-
, jose >= 0.8.4
48+
, generic-override >= 0.2.0.0
49+
, generic-override-aeson
4850
, http-client
4951
, http-client-tls
5052
, http-types
53+
, jose >= 0.8.4
5154
, lens
5255
, lens-aeson
5356
, mtl

0 commit comments

Comments
 (0)