biocad / openapi3

OpenAPI 3.0 data model
BSD 3-Clause "New" or "Revised" License
39 stars 53 forks source link

Support polymorphic types in schema generator #9

Closed maksbotan closed 3 years ago

maksbotan commented 3 years ago

This commit adds a way to generate schemas for types of kind Type -> Type, correctly setting the name for each concrete schema. Previously an attempt to use polymorphic types with ToSchema instance led to the same schema being duplicated for all concrete types.

Fixes https://github.com/biocad/servant-openapi3/issues/4

maksbotan commented 3 years ago

@Vic-m please take a look, as you are the one who requested it in servant-swagger repo :)

Vic-M commented 3 years ago

openapi_new.yml.txt

The issue still hasn't been fixed for my example. I've included the openapi yml file that gets generated.

Just to be sure, in order to use a haskell package at a specific commit, I should put

source-repository-package
   type: git
   location: https://github.com/biocad/openapi3.git
   tag: ad5f021c4e82c1d5dd47e18bdbdd8c48a3c4c2c2

in my cabal.project.local file right?

maksbotan commented 3 years ago

Sorry, I should've been more specific. That PR won't help you as is, I don't know of a nice way to make it work out of the box. Try something like this (it's all described in docs in the PR changes):

{-# LANGUAGE DataKinds          #-}
{-# LANGUAGE DeriveAnyClass     #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE DerivingVia        #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators      #-}

module Example where

import Data.Aeson
import Data.OpenApi    hiding (Server)
import Data.Text       (Text)
import GHC.Generics    (Generic)
import Type.Reflection (Typeable)

data Product
  = Product
      { id   :: Int
      , sku  :: Text
      , name :: Text
      }
  deriving (Generic)
instance ToJSON Product
instance ToSchema Product

data ProductCategory
  = ProductCategory
      { categoryId   :: Int
      , categoryName :: Text
      }
  deriving (Generic)
instance ToJSON ProductCategory
instance ToSchema ProductCategory

-- frontend expects all object properties to be wrapped in an "_embedded" key
data Embedded a
  = Embedded
      { _embedded :: a
      }
  deriving (Generic, ToSchema1)
instance ToJSON a => ToJSON (Embedded a)

deriving via BySchema1 Embedded a instance (ToSchema a, Typeable a) => ToSchema (Embedded a)

-- >>> _namedSchemaName $ undeclare $ declareNamedSchema @(Embedded ProductCategory) Proxy
-- Just "Embedded_ProductCategory"

-- >>> _namedSchemaName $ undeclare $ declareNamedSchema @(Embedded Product) Proxy
-- Just "Embedded_Product"
Vic-M commented 3 years ago

I tried your changes and it works! Thanks for taking the time to work on this.

maksbotan commented 3 years ago

Published at https://hackage.haskell.org/package/openapi3-3.0.1.0. Enjoy!