Skip to content
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

Break up HasSchemaResult to not require all constraints when only one is needed #74

Open
dewey92 opened this issue Jun 20, 2021 · 3 comments

Comments

@dewey92
Copy link

dewey92 commented Jun 20, 2021

Describe the bug

First of all, thanks for the amazing plugin 🙂 I just started picking up Haskell for my pet project so my knowledge is pretty limited. So to my understanding, this constraint https://github.com/LeapYear/aeson-schemas/blob/70fc7e2158c4dc2626cbc4ead5998bab126c3ac0/src/Data/Aeson/Schema/Internal.hs#L178 will make sure that every type used in the schema QuasiQuote should always have both FromJSON and ToJSON instance. I think this can be a bit problematic if, for instance, I have a password type that can be parsed but shouldn't be sent over the wire:

data Hash = Plain | Hashed

newtype Password (hash :: Hash) = Password Text

mkPlainPassword :: Text -> Either Text (Password 'Plain)
mkPlainPassword pw
  | T.length pw >= 8 = pure $ Password pw
  | otherwise = Left "Password should be at least 8 chars"

instance FromJSON (Password 'Plain) where
  parseJSON rawPw = do
    pw <- parseJSON rawPw
    case mkPlainPassword pw of
      Right p -> pure p
      Left e -> fail $ T.unpack e

type PasswordPlain = Password 'Plain
type PostNewUser =
  Object
    [schema|
  {
    username: Text,
    email: Text,
    password: PasswordPlain,
    fullname: Text,
  }
|]

At least as far as Servant is concerned, I got a type error saying that Password 'Plain should have ToJSON instance, while PostNewUser is only consumed as a request body and not as a response.

data User = ...

instance ToJSON User where
  toJSON u =
    object
      [ "username" .= user_username u
      , "email" .= user_email u
      , "fullname" .= user_fullname u
      ]

type SignUpAPI = ReqBody '[JSON] PostNewUser :> Post '[JSON] User

server :: Server SignupAPI
server = ...

-- | Error
-- 
--   No instance for (ToJSON (Password 'Plain)) arising from a use of `serve`
app = serve (Proxy @SignUpAPI) server

I'm not sure if this is Servant's specific problem or not, but perhaps you can help me give a clue

Expected behavior

I should be able to define a custom type without having ToJSON instance.

@brandon-leapyear
Copy link
Contributor

brandon-leapyear commented Jun 20, 2021 via email

@dewey92
Copy link
Author

dewey92 commented Jun 20, 2021

Thanks for the answer. From the resources read, it seems to me that many do request validation in FromJSON instance body, so that's why I do it that way. Perhaps because aeson already does a good job at parsing JSON types and allows us to convert them into user defined types. Anyway, good to know that user defined types indeed require at least both FromJSON and ToJSON instance 🙂

So, would you call it a bug? Or shall I close the issue?

@brandon-leapyear
Copy link
Contributor

brandon-leapyear commented Jun 21, 2021

This is an old work account. Please reference @brandonchinn178 for all future communication


Perhaps because aeson already does a good job at parsing JSON types and allows us to convert them into user defined types.

aeson is good at parsing JSON, but IMO it should only be used to get JSON into haskell-land, with not much logic in it. But that's just my preference.

So, would you call it a bug? Or shall I close the issue?

I would call this a feature request. It's working as intended, but it would be nice to not require all constraints.

@brandon-leapyear brandon-leapyear changed the title ToJSON instance is always necessary for user defined type Break up HasSchemaResult to not require all constraints when only one is needed Jun 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants