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

schema-profunctor: Add ToSchema instance for lists #1497

Merged
merged 10 commits into from
May 11, 2021

Conversation

akshaymankar
Copy link
Member

No description provided.

@akshaymankar akshaymankar requested a review from pcapriotti May 11, 2021 07:38
@@ -563,6 +563,8 @@ instance ToSchema String where schema = genericToSchema

instance ToSchema Bool where schema = genericToSchema

instance (S.ToSchema a, A.ToJSON a, A.FromJSON a)=> ToSchema [a] where schema = genericToSchema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this:

Suggested change
instance (S.ToSchema a, A.ToJSON a, A.FromJSON a)=> ToSchema [a] where schema = genericToSchema
instance ToSchema a => ToSchema [a] where schema = array schema

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is there a difference. This does look better, asking for my education.
I also pushed another instance for NonEmpty a I am guessing array wouldn't produce a nice ToSchema instance for that, so we should use genericToSchema in that case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I can see the difference, if we the one I made, it would ignore a ToSchema explicitly defined for a.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think genericToSchema should only be used for bootstrapping primitive types, but not for instances that recurse over previously defined schemas. There isn't a huge difference in this case, but maybe in the future we might get rid of the S.ToSchema instances altogether (and simply convert the top-level SchemaP's to a named swagger schema when we generate the swagger), in which case this wouldn't work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, array schema doesn't work as it doesn't produce a named schema, so either I need a TypeRep, which would be annoying or I need to set it to something like an empty string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're right, on both counts.

I think it's better not to have this instance. One can just use array when you need an array, since lists are indeed most commonly unnamed.

Similarly, I would add a nonEmptyArray combinator instead of the NonEmpty instance.

@akshaymankar akshaymankar force-pushed the akshaymankar/schema-list branch from e7f8495 to e984d23 Compare May 11, 2021 07:45
@@ -563,6 +564,10 @@ instance ToSchema String where schema = genericToSchema

instance ToSchema Bool where schema = genericToSchema

instance (S.ToSchema a, A.ToJSON a, A.FromJSON a) => ToSchema [a] where schema = genericToSchema

instance (S.ToSchema a, A.ToJSON a, A.FromJSON a) => ToSchema (NonEmpty a) where schema = genericToSchema
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also like to define this in terms of ToSchema a rather than S.ToSchema a. It should be possible to use array, and then withParser to convert it into a non-empty list.

…oSchema

Declaring instances using S.ToSchema and A.{To,From}JSON is a little problematic
as it wouldn't use an explicitly defined P.ToSchema, so instead of using
instances for P.ToSchema, users will be expected to use the combinators as
required.
Comment on lines 321 to 333
nonEmptyArray ::
forall ndoc doc a.(HasArray ndoc doc, HasName ndoc, S.HasMinItems doc (Maybe Integer)) =>
ValueSchema ndoc a ->
ValueSchema doc (NonEmpty a)
nonEmptyArray sch = SchemaP (SchemaDoc s) (SchemaIn r) (SchemaOut w)
where
name = maybe "non-empty array" ("non-empty array of " <>) (getName (schemaDoc sch))
r = A.withArray (T.unpack name) $ \arr -> case V.toList arr of
[] -> A.parseFail "Unexpected empty array found while parsing a NonEmpty"
(x : xs) -> mapM (schemaIn sch) (x :| xs)
s = mkArray (schemaDoc sch) & S.minItems ?~ 1
w xs = A.Array . V.fromList <$> mapM (schemaOut sch) (NonEmpty.toList xs)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use array instead of reimplementing its functionality. Something like:

nonEmptyArray sch = NonEmpty.toList .= array sch `withParser` check
  where
    check = maybe (fail "Unexpected empty array found while parsing a NonEmpty") pure
          . NonEmpty.nonEmpty

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't make the swagger work nicely, thought it was ok to have a small amount of duplication. Maybe we can look at it together after lunch to get it to work?

@@ -316,6 +318,19 @@ array sch = SchemaP (SchemaDoc s) (SchemaIn r) (SchemaOut w)
s = mkArray (schemaDoc sch)
w x = A.Array . V.fromList <$> mapM (schemaOut sch) x

nonEmptyArray ::
forall ndoc doc a.(HasArray ndoc doc, HasName ndoc, S.HasMinItems doc (Maybe Integer)) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super important, but the combinators do not depend on Swagger so far. Would it possible to have a locally defined type class to set the minimum, instead of using S.HasMinItems?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first added it to HasArray, but it didn't look very nice as the lens doesn't depend on the ndoc variable and it has to be type applied. I will defined a similar class, that should also work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this now.

Copy link
Contributor

@pcapriotti pcapriotti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the possible simplification of the minItems lens, looks good to me.

Comment on lines 523 to 527
instance HasMinItems SwaggerDoc (Maybe Integer) where
minItems =
lens
(\(WithDeclare _ s) -> s & view S.minItems)
(\(WithDeclare d s) mInt -> WithDeclare d $ s & S.minItems .~ mInt)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one could simply be a lens composition: minItems = declared . S.minItems or similar.

@akshaymankar akshaymankar merged commit b16ed84 into develop May 11, 2021
@akshaymankar akshaymankar deleted the akshaymankar/schema-list branch May 11, 2021 13:18
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

Successfully merging this pull request may close these issues.

2 participants