mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 04:24:35 +03:00
84fd5910b0
This PR expands the OpenAPI specification generated for metadata to include separate definitions for `SourceMetadata` for each native database type, and for DataConnector. For the most part the changes add `HasCodec` implementations, and don't modify existing code otherwise. The generated OpenAPI spec can be used to generate TypeScript definitions that distinguish different source metadata types based on the value of the `kind` properly. There is a problem: because the specified `kind` value for a data connector source is any string, when TypeScript gets a source with a `kind` value of, say, `"postgres"`, it cannot unambiguously determine whether the source is postgres, or a data connector. For example, ```ts function consumeSourceMetadata(source: SourceMetadata) { if (source.kind === "postgres" || source.kind === "pg") { // At this point TypeScript infers that `source` is either an instance // of `PostgresSourceMetadata`, or `DataconnectorSourceMetadata`. It // can't narrow further. source } if (source.kind === "something else") { // TypeScript infers that this `source` must be an instance of // `DataconnectorSourceMetadata` because `source.kind` does not match // any of the other options. source } } ``` The simplest way I can think of to fix this would be to add a boolean property to the `SourceMetadata` type along the lines of `isNative` or `isDataConnector`. This could be a field that only exists in serialized data, like the metadata version field. The combination of one of the native database names for `kind`, and a true value for `isNative` would be enough for TypeScript to unambiguously distinguish the source kinds. But note that in the current state TypeScript is able to reference the short `"pg"` name correctly! ~~Tests are not passing yet due to some discrepancies in DTO serialization vs existing Metadata serialization. I'm working on that.~~ The placeholders that I used for table and function metadata are not compatible with the ordered JSON serialization in use. I think the best solution is to write compatible codecs for those types in another PR. For now I have disabled some DTO tests for this PR. Here are the generated [OpenAPI spec](https://github.com/hasura/graphql-engine-mono/files/9397333/openapi.tar.gz) based on these changes, and the generated [TypeScript client code](https://github.com/hasura/graphql-engine-mono/files/9397339/client-typescript.tar.gz) based on that spec. Ticket: [MM-66](https://hasurahq.atlassian.net/browse/MM-66) PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5582 GitOrigin-RevId: e1446191c6c832879db04f129daa397a3be03f62
92 lines
4.2 KiB
Haskell
92 lines
4.2 KiB
Haskell
-- | We are in the process of building DTO types incrementally. We use
|
|
-- placeholder types in positions in data structures that are not fully-defined
|
|
-- yet. For example 'PlaceholderObject' represents some unspecified JSON object,
|
|
-- and 'PlaceholderArray' represents an array whose contents are not yet
|
|
-- specified.
|
|
--
|
|
-- We are transitioning from converting 'Hasura.RQL.Types.Metadata' directly to
|
|
-- JSON to converting it to 'Hasura.Server.API.DTO.Metadata.MetadataDTO'
|
|
-- instead. Serialization and deserialization for placeholder values is
|
|
-- delegated to the old JSON serialization code.
|
|
module Hasura.Metadata.DTO.Placeholder
|
|
( PlaceholderArray (..),
|
|
PlaceholderObject (..),
|
|
IsPlaceholder (..),
|
|
placeholderCodecViaJSON,
|
|
)
|
|
where
|
|
|
|
import Autodocodec (Autodocodec, HasCodec (codec), JSONCodec, bimapCodec, codecViaAeson, dimapCodec, valueCodec, vectorCodec, (<?>))
|
|
import Autodocodec.OpenAPI ()
|
|
import Data.Aeson (FromJSON, ToJSON)
|
|
import Data.Aeson qualified as JSON
|
|
import Data.Aeson.Ordered qualified as AO
|
|
import Data.Aeson.Types qualified as JSON
|
|
import Data.OpenApi qualified as OpenApi
|
|
import Data.Vector qualified as V
|
|
import Hasura.Prelude
|
|
|
|
-- TODO: Store ordered aeson values in placeholders instead of stock aeson
|
|
-- values so that we can preserve order. We want to do that after #4842 is
|
|
-- merged so we can use 'toOrderedJSONVia' to produce the appropriate codecs.
|
|
|
|
-- | Stands in for an array that we have not had time to fully specify yet.
|
|
-- Generated OpenAPI documentation for 'PlaceholderArray' will permit an array
|
|
-- of values of any type, and a note will be appended to the documentation
|
|
-- string for the value explaining that this is a temporary placeholder.
|
|
newtype PlaceholderArray = PlaceholderArray JSON.Array
|
|
deriving newtype (Show, Eq, FromJSON, ToJSON)
|
|
deriving stock (Generic)
|
|
deriving (OpenApi.ToSchema) via (Autodocodec PlaceholderArray)
|
|
|
|
-- | Stands in for an object that we have not had time to fully specify yet.
|
|
-- Generated OpenAPI documentation for 'PlaceholderObject' will permit an object
|
|
-- with any keys with any types of values. A note will be appended to the
|
|
-- documentation string for the value explaining that this is a temporary
|
|
-- placeholder.
|
|
newtype PlaceholderObject = PlaceholderObject JSON.Object
|
|
deriving newtype (Show, Eq, FromJSON, ToJSON)
|
|
deriving stock (Generic)
|
|
deriving (OpenApi.ToSchema) via (Autodocodec PlaceholderObject)
|
|
|
|
instance HasCodec PlaceholderArray where
|
|
codec = dimapCodec mapOutput mapInput (vectorCodec valueCodec) <?> documentation
|
|
where
|
|
mapOutput = PlaceholderArray
|
|
mapInput (PlaceholderArray a) = a
|
|
documentation =
|
|
"\n\narray of values of unspecified type - this is a placeholder that will eventually be replaced with a more detailed description"
|
|
|
|
instance HasCodec PlaceholderObject where
|
|
codec = codecViaAeson "\n\nobject with unspecified properties - this is a placeholder that will eventually be replaced with a more detailed description"
|
|
|
|
class IsPlaceholder p a | a -> p where
|
|
-- | Use this function to mark an Aeson type (Array or Object) as
|
|
-- a temporary placeholder in a larger data structure.
|
|
placeholder :: a -> p
|
|
|
|
instance IsPlaceholder PlaceholderArray JSON.Array where
|
|
placeholder = PlaceholderArray
|
|
|
|
instance IsPlaceholder PlaceholderObject JSON.Object where
|
|
placeholder = PlaceholderObject
|
|
|
|
instance IsPlaceholder PlaceholderArray AO.Array where
|
|
placeholder = PlaceholderArray . V.fromList . map AO.fromOrdered . V.toList
|
|
|
|
instance IsPlaceholder PlaceholderObject AO.Object where
|
|
placeholder = PlaceholderObject . AO.fromOrderedObject
|
|
|
|
-- | This placeholder can be used in a codec to represent any type of data that
|
|
-- has `FromJSON` and `ToJSON` instances. Generated OpenAPI specifications based
|
|
-- on this codec will not show any information about the internal structure of
|
|
-- the type so ideally uses of this placeholder should eventually be replaced
|
|
-- with more descriptive codecs.
|
|
placeholderCodecViaJSON :: (FromJSON a, ToJSON a) => JSONCodec a
|
|
placeholderCodecViaJSON =
|
|
bimapCodec dec enc valueCodec
|
|
<?> "value with unspecified type - this is a placeholder that will eventually be replaced with a more detailed description"
|
|
where
|
|
dec = JSON.parseEither JSON.parseJSON
|
|
enc = JSON.toJSON
|