-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSON keys examples in a Schema of (Map UUID Something)? #136
Comments
Yes, this can be done using instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
declareNamedSchema _ = case toJSONKey :: ToJSONKeyFunction k of
ToJSONKeyText _ _ -> declareObjectMapSchema
ToJSONKeyValue _ _ -> declareNamedSchema (Proxy :: Proxy [(k, v)])
where
valueExample = toSchema (Proxy :: Proxy v) ^? example
keyExample = case toSchema (Proxy :: Proxy k) ^? example of
Just (String s) -> Just s
_ -> Just "<key>" -- default key example can be useful when valueExample is not Nothing
declareObjectMapSchema = do
mKeyExample <- toSchema (Proxy :: Proxy k) ^? example
schema <- declareSchemaRef (Proxy :: Proxy v)
return $ unnamed $ mempty
& type_ .~ SwaggerObject
& additionalProperties ?~ schema
& example .~ (toJSON . Map.singleton) <$> keyExample <*> valueExample @michalrus would you like to submit a PR with examples constructed this way for various maps? |
After another bug in our client code, I’m getting back to this issue. Sorry it took so long. The I’m thinking, could we also state somehow what type the keys are? For example if we have this: newtype UserId = UserId UUID deriving (Eq, Generic)
newtype GroupId = GroupId UUID deriving (Eq, Generic)
instance ToSchema UserId
instance ToSchema GroupId … and then if we make some endpoint return a We have that if |
In both Swagger 2.0 and OpenAPI 3.0 keys are strings. The only way you can mention key type is via documentation (at least for most key types). However, if your key type is an enumeration type and has finitely many values, you can do something like this: -- | Produce a schema for a map with finitely many possible keys.
declareMapSchemaWithEnumBoundedKey
:: forall k v. (Enum k, Bounded k, ToJSONKey k, ToSchema v)
=> Proxy k
-> Proxy v
-> Declare (Definitions Schema) Schema
declareMapSchemaWithEnumBoundedKey pk pv = do
valueSchema <- declareSchemaRef pv
case toJSONKey @k of
ToJSONKeyValue _ _ -> error "keys should be strings!"
ToJSONKeyText keyToText _ -> do
let ps = map (\k -> (keyToText k, valueSchema))
[minBound..maxBound :: k]
return $ mempty
& type_ .~ SwaggerObject
& properties .~ InsOrdHashMap.fromList ps Here's usage example: data Key = Up | Down | Space | Esc
deriving (Generic, Show, Enum, Bounded, ToJSON)
instance ToJSONKey Key where
toJSONKey = ToJSONKeyText (Text.pack . show) (Aeson.text . Text.pack . show)
type Action = String
newtype Controls = Controls (Map Key Action)
instance ToSchema Controls where
declareNamedSchema _ = NamedSchema (Just "Controls")
<$> declareMapSchemaWithEnumBoundedKey @Key @Action Proxy Proxy
|
Yes, clear. So your first suggestion won’t work for complex types in values (which don’t have an But I used the second suggestion to come up with this: diff --git a/src/Data/Swagger/Internal/Schema.hs b/src/Data/Swagger/Internal/Schema.hs
index 927e9e6..b63895a 100644
--- a/src/Data/Swagger/Internal/Schema.hs
+++ b/src/Data/Swagger/Internal/Schema.hs
@@ -61,6 +61,7 @@ import Data.Version (Version)
import Numeric.Natural.Compat (Natural)
import Data.Word
import GHC.Generics
+import Data.Typeable (Typeable, typeRep)
import qualified Data.UUID.Types as UUID
import Data.Swagger.Declare
@@ -520,18 +521,26 @@ instance ToSchema a => ToSchema (IntMap a) where
declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
#if MIN_VERSION_aeson(1,0,0)
-instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
+instance (Typeable k, Typeable v, ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
declareNamedSchema _ = case toJSONKey :: ToJSONKeyFunction k of
ToJSONKeyText _ _ -> declareObjectMapSchema
ToJSONKeyValue _ _ -> declareNamedSchema (Proxy :: Proxy [(k, v)])
where
+ keyExample = case toSchema (Proxy :: Proxy k) ^. example of
+ Just (String s) -> s
+ _ -> "*"
+ keyType = T.pack . show . typeRep $ (Proxy :: Proxy k)
+ mapName = T.pack . show . typeRep $ (Proxy :: Proxy (Map k v))
declareObjectMapSchema = do
- schema <- declareSchemaRef (Proxy :: Proxy v)
+ valueSchema <- declareSchemaRef (Proxy :: Proxy v)
+ keySchema <- declareSchemaRef (Proxy :: Proxy k)
return $ unnamed $ mempty
& type_ .~ SwaggerObject
- & additionalProperties ?~ schema
+ & description ?~ mapName
+ & properties .~ [(keyExample <> " @" <> keyType, valueSchema)]
+ & additionalProperties ?~ valueSchema
-instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
+instance (Typeable k, Typeable v, ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Map k v))
#else So a description of that Map is set to its type name. Also we add a single artificial property which contains a sample key value (or It’s not ideal, but enough for consumers to understand what’s happening. =) |
Sometimes it’s useful to return a
Map k v
from an API, e.g. when thisk
is some kind of a newtype over UUID, or similar.Anyway, in such a case, the generated example value looks like:
while the real response:
Perhaps it’d possible to have:
?
Now, the generated swagger.json contains:
The text was updated successfully, but these errors were encountered: