morpheus-graphql-client 0.21.0 → 0.22.0
raw patch · 13 files changed
+386/−598 lines, 13 filesdep +morpheus-graphql-code-gen-utilsdep −morpheus-graphql-code-gendep ~morpheus-graphql-core
Dependencies added: morpheus-graphql-code-gen-utils
Dependencies removed: morpheus-graphql-code-gen
Dependency ranges changed: morpheus-graphql-core
Files
- morpheus-graphql-client.cabal +7/−10
- src/Data/Morpheus/Client/Declare.hs +8/−12
- src/Data/Morpheus/Client/Declare/Aeson.hs +0/−264
- src/Data/Morpheus/Client/Declare/Client.hs +0/−30
- src/Data/Morpheus/Client/Declare/RequestType.hs +0/−55
- src/Data/Morpheus/Client/Declare/Type.hs +0/−85
- src/Data/Morpheus/Client/Internal/TH.hs +61/−53
- src/Data/Morpheus/Client/Internal/Types.hs +22/−24
- src/Data/Morpheus/Client/Internal/Utils.hs +30/−4
- src/Data/Morpheus/Client/Printing/TH.hs +152/−0
- src/Data/Morpheus/Client/Transform/Core.hs +45/−2
- src/Data/Morpheus/Client/Transform/Global.hs +27/−21
- src/Data/Morpheus/Client/Transform/Local.hs +34/−38
morpheus-graphql-client.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4.+-- This file has been generated from package.yaml by hpack version 0.35.0. -- -- see: https://github.com/sol/hpack name: morpheus-graphql-client-version: 0.21.0+version: 0.22.0 synopsis: Morpheus GraphQL Client description: Build GraphQL APIs with your favorite functional language! category: web, graphql, client@@ -57,10 +57,6 @@ Data.Morpheus.Client other-modules: Data.Morpheus.Client.Declare- Data.Morpheus.Client.Declare.Aeson- Data.Morpheus.Client.Declare.Client- Data.Morpheus.Client.Declare.RequestType- Data.Morpheus.Client.Declare.Type Data.Morpheus.Client.Fetch Data.Morpheus.Client.Fetch.GQLClient Data.Morpheus.Client.Fetch.Http@@ -70,6 +66,7 @@ Data.Morpheus.Client.Internal.TH Data.Morpheus.Client.Internal.Types Data.Morpheus.Client.Internal.Utils+ Data.Morpheus.Client.Printing.TH Data.Morpheus.Client.QuasiQuoter Data.Morpheus.Client.Schema.JSON.Parse Data.Morpheus.Client.Schema.JSON.TypeKind@@ -91,8 +88,8 @@ , containers >=0.4.2.1 && <0.7.0 , file-embed >=0.0.10 && <1.0.0 , modern-uri >=0.1.0.0 && <1.0.0- , morpheus-graphql-code-gen >=0.21.0 && <0.22.0- , morpheus-graphql-core >=0.21.0 && <0.22.0+ , morpheus-graphql-code-gen-utils+ , morpheus-graphql-core >=0.22.0 && <0.23.0 , morpheus-graphql-subscriptions , mtl >=2.0.0 && <3.0.0 , relude >=0.3.0 && <2.0.0@@ -132,8 +129,8 @@ , file-embed >=0.0.10 && <1.0.0 , modern-uri >=0.1.0.0 && <1.0.0 , morpheus-graphql-client- , morpheus-graphql-code-gen >=0.21.0 && <0.22.0- , morpheus-graphql-core >=0.21.0 && <0.22.0+ , morpheus-graphql-code-gen-utils+ , morpheus-graphql-core >=0.22.0 && <0.23.0 , morpheus-graphql-subscriptions , mtl >=2.0.0 && <3.0.0 , relude >=0.3.0 && <2.0.0
src/Data/Morpheus/Client/Declare.hs view
@@ -12,23 +12,23 @@ ) where -import Data.Morpheus.Client.Declare.Client (declareTypes)-import Data.Morpheus.Client.Declare.RequestType (declareRequestType) import Data.Morpheus.Client.Internal.Types ( ExecutableSource, SchemaSource, ) import Data.Morpheus.Client.Internal.Utils (getFile, getSource, handleResult)+import Data.Morpheus.Client.Printing.TH+ ( printDeclarations,+ ) import Data.Morpheus.Client.QuasiQuoter (raw) import Data.Morpheus.Client.Schema.Parse (parseSchema) import Data.Morpheus.Client.Transform ( toGlobalDefinitions, toLocalDefinitions, )-import Data.Morpheus.CodeGen.Internal.AST import Data.Morpheus.Core (parseRequest) import Data.Morpheus.Types.IO (GQLRequest (..))-import Data.Set+import Data.Morpheus.Types.Internal.AST (TypeName) import qualified Data.Set as S import Language.Haskell.TH (Dec, Q, runIO) import Relude@@ -46,16 +46,12 @@ ( do schemaDoc <- parseSchema schemaText executableDoc <- parseRequest request- toLocalDefinitions executableDoc schemaDoc- )- ( \(fetch, types) ->- (<>)- <$> declareRequestType query fetch- <*> declareTypes types+ toLocalDefinitions (query, executableDoc) schemaDoc )+ printDeclarations globalTypeDeclarations :: SchemaSource -> (TypeName -> Bool) -> Q [Dec]-globalTypeDeclarations src f = handleResult (toGlobalDefinitions f <$> parseSchema src) declareTypes+globalTypeDeclarations src f = handleResult (toGlobalDefinitions f <$> parseSchema src) printDeclarations -- | declares global or local types, depending -- on whether the second argument is specified or not@@ -93,7 +89,7 @@ declareGlobalTypesByName :: FilePath -> [TypeName] -> Q [Dec] declareGlobalTypesByName path names = do schema <- getSource path- globalTypeDeclarations schema (`member` S.fromList names)+ globalTypeDeclarations schema (`S.member` S.fromList names) {- ORMOLU_DISABLE -} -- | declares object, interface and union types for
− src/Data/Morpheus/Client/Declare/Aeson.hs
@@ -1,264 +0,0 @@-{-# LANGUAGE ConstrainedClassMethods #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskellQuotes #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Client.Declare.Aeson- ( aesonDeclarations,- )-where--import Data.Aeson-import Data.Aeson.Types-import Data.Morpheus.Client.Internal.TH- ( decodeObjectE,- destructRecord,- failExp,- hasInstance,- isTypeDeclared,- matchWith,- mkFieldsE,- )-import Data.Morpheus.Client.Internal.Types- ( ClientConstructorDefinition (..),- ClientTypeDefinition (..),- TypeNameTH (..),- )-import Data.Morpheus.CodeGen.Internal.TH- ( applyCons,- camelCaseTypeName,- funDSimple,- toCon,- toName,- toString,- v',- _',- )-import Data.Morpheus.Internal.Utils (IsMap (lookup))-import Data.Morpheus.Types.GQLScalar- ( scalarFromJSON,- scalarToJSON,- )-import Data.Morpheus.Types.Internal.AST- ( FieldName,- GQLError,- Msg (msg),- TypeKind (..),- TypeName,- internal,- isResolverType,- )-import qualified Data.Text as T-import Language.Haskell.TH- ( DecQ,- Exp (..),- ExpQ,- Name,- PatQ,- Q,- appE,- conP,- cxt,- instanceD,- tupP,- )-import Language.Haskell.TH.Syntax (Dec)-import Relude hiding (toString)--aesonDeclarations :: TypeKind -> ClientTypeDefinition -> Q [Dec]-aesonDeclarations KindEnum clientDef = do- a <- deriveIfNotDefined deriveFromJSON ''FromJSON clientDef- b <- deriveIfNotDefined deriveToJSON ''ToJSON clientDef- pure (a <> b)-aesonDeclarations KindScalar clientDef = deriveScalarJSON clientDef-aesonDeclarations kind clientDef- | isResolverType kind = deriveIfNotDefined deriveFromJSON ''FromJSON clientDef- | otherwise = deriveIfNotDefined deriveToJSON ''ToJSON clientDef--deriveIfNotDefined :: (ClientTypeDefinition -> Q Dec) -> Name -> ClientTypeDefinition -> Q [Dec]-deriveIfNotDefined derivation typeClass clientDef = do- exists <- isTypeDeclared clientDef- if exists- then do- has <- hasInstance typeClass clientDef- if has- then pure []- else mkDerivation- else mkDerivation- where- mkDerivation :: Q [Dec]- mkDerivation = do- derived <- derivation clientDef- pure [derived]--failure :: GQLError -> Q a-failure = fail . show--deriveScalarJSON :: ClientTypeDefinition -> Q [Dec]-deriveScalarJSON clientDef = do- a <- deriveIfNotDefined deriveScalarFromJSON ''FromJSON clientDef- b <- deriveIfNotDefined deriveScalarToJSON ''ToJSON clientDef- pure (a <> b)--deriveScalarFromJSON :: ClientTypeDefinition -> DecQ-deriveScalarFromJSON ClientTypeDefinition {clientTypeName} =- defineFromJSON clientTypeName [|scalarFromJSON|]--deriveScalarToJSON :: ClientTypeDefinition -> DecQ-deriveScalarToJSON- ClientTypeDefinition- { clientTypeName = TypeNameTH {typename}- } = instanceD (cxt []) typeDef body- where- typeDef = applyCons ''ToJSON [typename]- body = [funDSimple 'toJSON [] [|scalarToJSON|]]---- FromJSON-deriveFromJSON :: ClientTypeDefinition -> DecQ-deriveFromJSON ClientTypeDefinition {clientCons = [], clientTypeName} =- failure $- internal $- "Type "- <> msg (typename clientTypeName)- <> " Should Have at least one Constructor"-deriveFromJSON ClientTypeDefinition {clientTypeName, clientCons, clientKind = KindEnum} =- defineFromJSON clientTypeName $- aesonFromJSONEnumBody clientTypeName clientCons-deriveFromJSON- ClientTypeDefinition- { clientTypeName = clientTypeName@TypeNameTH {namespace},- clientCons = [cons]- } =- defineFromJSON clientTypeName $- aesonObject namespace cons-deriveFromJSON typeD@ClientTypeDefinition {clientTypeName} =- defineFromJSON clientTypeName $- aesonUnionObject typeD--aesonObject :: [FieldName] -> ClientConstructorDefinition -> ExpQ-aesonObject tNamespace con@ClientConstructorDefinition {cName} =- withBody- <$> aesonObjectBody tNamespace con- where- withBody body =- AppE- (AppE (VarE 'withObject) name)- (LamE [v'] body)- name :: Exp- name = toString (camelCaseTypeName tNamespace cName)--aesonObjectBody :: [FieldName] -> ClientConstructorDefinition -> ExpQ-aesonObjectBody namespace ClientConstructorDefinition {cName, cFields} =- decodeObjectE- entry- (camelCaseTypeName namespace cName)- cFields--entry :: Bool -> Name-entry nullable- | nullable = '(.:?)- | otherwise = '(.:)--aesonUnionObject :: ClientTypeDefinition -> ExpQ-aesonUnionObject- ClientTypeDefinition- { clientCons,- clientTypeName = TypeNameTH {namespace, typename}- } =- appE [|takeValueType|] $- matchWith elseCondition f clientCons- where- elseCondition =- (tupP [_', v'],)- . aesonObjectBody- namespace- <$> find ((typename ==) . cName) clientCons- f cons@ClientConstructorDefinition {cName, cFields} =- ( tupP [toString cName, if null cFields then _' else v'],- aesonObjectBody namespace cons- )--takeValueType :: ((String, Object) -> Parser a) -> Value -> Parser a-takeValueType f (Object hMap) = case lookup "__typename" hMap of- Nothing -> fail "key \"__typename\" not found on object"- Just (String x) -> f (T.unpack x, hMap)- Just val ->- fail $ "key \"__typename\" should be string but found: " <> show val-takeValueType _ _ = fail "expected Object"--namespaced :: TypeNameTH -> TypeName-namespaced TypeNameTH {namespace, typename} =- camelCaseTypeName namespace typename--defineFromJSON :: TypeNameTH -> ExpQ -> DecQ-defineFromJSON name expr = instanceD (cxt []) typeDef body- where- typeDef = applyCons ''FromJSON [namespaced name]- body = [funDSimple 'parseJSON [] expr]--aesonFromJSONEnumBody :: TypeNameTH -> [ClientConstructorDefinition] -> ExpQ-aesonFromJSONEnumBody TypeNameTH {typename} = matchWith (Just (v', failExp)) f- where- f :: ClientConstructorDefinition -> (PatQ, ExpQ)- f ClientConstructorDefinition {cName} =- ( toString cName,- appE [|pure|] $ toCon $ camelCaseTypeName [typename] cName- )--aesonToJSONEnumBody :: TypeNameTH -> [ClientConstructorDefinition] -> ExpQ-aesonToJSONEnumBody TypeNameTH {typename} = matchWith Nothing f- where- f :: ClientConstructorDefinition -> (PatQ, ExpQ)- f ClientConstructorDefinition {cName} =- ( conP (toName $ camelCaseTypeName [typename] cName) [],- toString cName- )---- ToJSON-deriveToJSON :: ClientTypeDefinition -> DecQ-deriveToJSON- ClientTypeDefinition- { clientCons = []- } =- fail "Type Should Have at least one Constructor"-deriveToJSON- ClientTypeDefinition- { clientTypeName = clientTypeName@TypeNameTH {typename},- clientCons,- clientKind = KindEnum- } = instanceD (cxt []) typeDef body- where- typeDef = applyCons ''ToJSON [typename]- body = [funDSimple 'toJSON [] (aesonToJSONEnumBody clientTypeName clientCons)]-deriveToJSON- ClientTypeDefinition- { clientTypeName = TypeNameTH {typename},- clientCons = [ClientConstructorDefinition {cFields}]- } =- instanceD (cxt []) appHead methods- where- appHead = applyCons ''ToJSON [typename]- ------------------------------------------------------------------- -- defines: toJSON (User field1 field2 ...)= object ["name" .= name, "age" .= age, ...]- methods = [funDSimple 'toJSON args body]- where- args = [destructRecord typename cFields]- body =- pure $- AppE- (VarE 'omitNulls)- (mkFieldsE typename '(.=) cFields)-deriveToJSON _ = fail "Input Unions are not yet supported"--omitNulls :: [Pair] -> Value-omitNulls = object . filter notNull- where- notNull (_, Null) = False- notNull _ = True
− src/Data/Morpheus/Client/Declare/Client.hs
@@ -1,30 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Client.Declare.Client- ( declareTypes,- )-where--import Data.Morpheus.Client.Declare.Aeson- ( aesonDeclarations,- )-import Data.Morpheus.Client.Declare.Type- ( typeDeclarations,- )-import Data.Morpheus.Client.Internal.Types- ( ClientTypeDefinition (..),- )-import Language.Haskell.TH-import Relude hiding (Type)--declareTypes :: [ClientTypeDefinition] -> Q [Dec]-declareTypes subTypes = concat <$> traverse declareType subTypes--declareType :: ClientTypeDefinition -> Q [Dec]-declareType clientType@ClientTypeDefinition {clientKind} = do- types <- typeDeclarations clientKind clientType- instances <- aesonDeclarations clientKind clientType- pure (types <> instances)
− src/Data/Morpheus/Client/Declare/RequestType.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE ConstrainedClassMethods #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskellQuotes #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Client.Declare.RequestType- ( declareRequestType,- )-where--import Data.Morpheus.Client.Fetch.RequestType- ( RequestType (..),- )-import Data.Morpheus.Client.Internal.Types- ( FetchDefinition (..),- TypeNameTH (..),- )-import Data.Morpheus.CodeGen.Internal.TH- ( applyCons,- funDSimple,- toCon,- typeInstanceDec,- _',- )-import qualified Data.Text as T-import Language.Haskell.TH- ( Dec,- Q,- Type,- cxt,- instanceD,- )-import Relude hiding (ByteString, Type)--declareRequestType :: Text -> FetchDefinition -> Q [Dec]-declareRequestType query FetchDefinition {clientArgumentsTypeName, rootTypeName, fetchOperationType} =- pure <$> instanceD (cxt []) iHead methods- where- queryString = T.unpack query- typeName = typename rootTypeName- iHead = applyCons ''RequestType [typeName]- methods =- [ funDSimple '__name [_'] [|typeName|],- funDSimple '__query [_'] [|queryString|],- funDSimple '__type [_'] [|fetchOperationType|],- pure $ typeInstanceDec ''RequestArgs (toCon typeName) (argumentType clientArgumentsTypeName)- ]--argumentType :: Maybe TypeNameTH -> Type-argumentType Nothing = toCon ("()" :: String)-argumentType (Just clientTypeName) = toCon (typename clientTypeName)
− src/Data/Morpheus/Client/Declare/Type.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskellQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Client.Declare.Type- ( typeDeclarations,- )-where--import Data.Morpheus.Client.Internal.TH (isTypeDeclared)-import Data.Morpheus.Client.Internal.Types- ( ClientConstructorDefinition (..),- ClientTypeDefinition (..),- TypeNameTH (..),- )-import Data.Morpheus.Client.Internal.Utils- ( isEnum,- )-import Data.Morpheus.CodeGen.Internal.TH- ( camelCaseTypeName,- declareTypeRef,- toCon,- toName,- )-import Data.Morpheus.Types.Internal.AST- ( ANY,- FieldDefinition (..),- FieldName,- TypeKind (..),- TypeName,- VALID,- )-import Language.Haskell.TH-import Relude hiding (Type)--typeDeclarations :: TypeKind -> ClientTypeDefinition -> Q [Dec]-typeDeclarations KindScalar _ = pure []-typeDeclarations _ c = do- exists <- isTypeDeclared c- if exists- then pure []- else pure [declareType c]--declareType :: ClientTypeDefinition -> Dec-declareType- ClientTypeDefinition- { clientTypeName = thName@TypeNameTH {namespace, typename},- clientCons- } =- DataD- []- (mkConName namespace typename)- []- Nothing- (declareCons thName clientCons)- (map derive [''Generic, ''Show, ''Eq])- where- derive className = DerivClause Nothing [ConT className]--declareCons :: TypeNameTH -> [ClientConstructorDefinition] -> [Con]-declareCons TypeNameTH {namespace, typename} clientCons- | isEnum clientCons = map consE clientCons- | otherwise = map consR clientCons- where- consE ClientConstructorDefinition {cName} = NormalC (mkTypeName namespace typename cName) []- consR ClientConstructorDefinition {cName, cFields} =- RecC- (mkConName namespace cName)- (map declareField cFields)--declareField :: FieldDefinition ANY VALID -> (Name, Bang, Type)-declareField FieldDefinition {fieldName, fieldType} =- ( toName fieldName,- Bang NoSourceUnpackedness NoSourceStrictness,- declareTypeRef toCon fieldType- )--mkTypeName :: [FieldName] -> TypeName -> TypeName -> Name-mkTypeName namespace typename = mkConName namespace . camelCaseTypeName [typename]--mkConName :: [FieldName] -> TypeName -> Name-mkConName namespace = toName . camelCaseTypeName namespace
src/Data/Morpheus/Client/Internal/TH.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskellQuotes #-} {-# LANGUAGE NoImplicitPrelude #-}@@ -15,29 +16,31 @@ ( matchWith, decodeObjectE, mkFieldsE,- destructRecord, failExp,- isTypeDeclared,- hasInstance,+ deriveIfNotDefined,+ declareIfNotDeclared, ) where +import Data.Aeson ((.:))+import Data.Aeson.Types ((.:?)) import Data.Foldable (foldr1)-import Data.Morpheus.Client.Internal.Types (ClientTypeDefinition (..), TypeNameTH (..))-import Data.Morpheus.CodeGen.Internal.TH- ( camelCaseFieldName,- camelCaseTypeName,- toCon,+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenConstructor (..),+ CodeGenField (..),+ CodeGenType (cgTypeName),+ CodeGenTypeName (..),+ getFullName,+ )+import Data.Morpheus.CodeGen.TH+ ( toCon, toName, toString, toVar, v',- vars, )-import Data.Morpheus.Types.Internal.AST- ( FieldDefinition (..),- TypeName,- isNullable,+import Data.Morpheus.CodeGen.Utils+ ( camelCaseFieldName, ) import Language.Haskell.TH import Relude hiding (toString)@@ -65,20 +68,26 @@ (stringE " is Not Valid Union Constructor") ) -decodeObjectE :: (Bool -> Name) -> TypeName -> [FieldDefinition cat s] -> ExpQ-decodeObjectE _ conName [] = appE [|pure|] (toCon conName)-decodeObjectE funName conName fields =- uInfixE- (toCon conName)- [|(<$>)|]- (foldr1 withApplicative $ map (defField funName) fields)+decodeObjectE :: CodeGenConstructor -> ExpQ+decodeObjectE CodeGenConstructor {..}+ | null constructorFields = appE [|pure|] (toCon constructorName)+ | otherwise =+ uInfixE+ (toCon constructorName)+ [|(<$>)|]+ (foldr1 withApplicative $ map defField constructorFields) +defField :: CodeGenField -> ExpQ+defField CodeGenField {..} = uInfixE v' (varE $ bindField fieldIsNullable) (toString fieldName)++bindField :: Bool -> Name+bindField nullable+ | nullable = '(.:?)+ | otherwise = '(.:)+ withApplicative :: ExpQ -> ExpQ -> ExpQ withApplicative x = uInfixE x [|(<*>)|] -defField :: (Bool -> Name) -> FieldDefinition cat s -> ExpQ-defField f field@FieldDefinition {fieldName} = uInfixE v' (varE $ f (isNullable field)) (toString fieldName)- -- | 'mkFieldsE' -- -- input :@@ -92,50 +101,49 @@ -- .. -- ] -- >>>-mkFieldsE :: TypeName -> Name -> [FieldDefinition cat s] -> Exp+mkFieldsE :: CodeGenTypeName -> Name -> [CodeGenField] -> Exp mkFieldsE conName name = ListE . map (mkEntryWith conName name) -- input : mkFieldWith 'mkValue (FieldDefinition { fieldName = "field1", ..}) -- expression: mkValue "field1" field1 mkEntryWith ::- TypeName ->+ CodeGenTypeName -> Name ->- FieldDefinition cat s ->+ CodeGenField -> Exp-mkEntryWith conName f FieldDefinition {fieldName} =+mkEntryWith conName f CodeGenField {fieldName} = AppE (AppE (VarE f) (toString fieldName))- (toVar $ camelCaseFieldName conName fieldName)---- |--- input:--- >>>--- WAS WAS destructRecord "User" ["name","id"]--- >>>------ expression:--- >>>--- WAS WAS (User name id)--- >>>-destructRecord :: TypeName -> [FieldDefinition cat s] -> PatQ-destructRecord conName fields = conP (toName conName) (vars names)- where- names = map (camelCaseFieldName conName . fieldName) fields+ (toVar $ camelCaseFieldName (getFullName conName) fieldName) -isTypeDeclared :: ClientTypeDefinition -> Q Bool-isTypeDeclared clientDef = do- let name = mkTypeName clientDef+isTypeDeclared :: CodeGenTypeName -> Q Bool+isTypeDeclared clientTypeName = do+ let name = toName clientTypeName m <- lookupTypeName (show name) case m of Nothing -> pure False _ -> pure True -hasInstance :: Name -> ClientTypeDefinition -> Q Bool-hasInstance typeClass clientDef = do- isInstance typeClass [ConT (mkTypeName clientDef)]+hasInstance :: Name -> CodeGenType -> Q Bool+hasInstance typeClass clientDef = isInstance typeClass [ConT (toName (cgTypeName clientDef))] -mkTypeName :: ClientTypeDefinition -> Name-mkTypeName ClientTypeDefinition {clientTypeName = TypeNameTH namespace typeName} =- toType typeName+deriveIfNotDefined :: (CodeGenType -> Q Dec) -> Name -> CodeGenType -> Q [Dec]+deriveIfNotDefined derivation typeClass clientDef = do+ exists <- isTypeDeclared (cgTypeName clientDef)+ if exists+ then do+ has <- hasInstance typeClass clientDef+ if has+ then pure []+ else mkDerivation+ else mkDerivation where- toType = toName . camelCaseTypeName namespace+ mkDerivation :: Q [Dec]+ mkDerivation = pure <$> derivation clientDef++declareIfNotDeclared :: (CodeGenType -> a) -> CodeGenType -> Q [a]+declareIfNotDeclared f c = do+ exists <- isTypeDeclared (cgTypeName c)+ if exists+ then pure []+ else pure [f c]
src/Data/Morpheus/Client/Internal/Types.hs view
@@ -5,52 +5,50 @@ module Data.Morpheus.Client.Internal.Types ( ClientTypeDefinition (..),- TypeNameTH (..),- FetchDefinition (..),- ClientConstructorDefinition (..),+ RequestTypeDefinition (..), FetchError (..), SchemaSource (..), ExecutableSource, GQLClientResult,+ ClientDeclaration (..),+ DERIVING_MODE (..), ) where import Data.ByteString.Lazy (ByteString)+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenConstructor (..),+ CodeGenType,+ CodeGenTypeName,+ ) import Data.Morpheus.Types.Internal.AST- ( ANY,- FieldDefinition,- FieldName,- GQLErrors,+ ( GQLErrors, OperationType, TypeKind, TypeName,- VALID, ) import Relude hiding (ByteString) -data TypeNameTH = TypeNameTH- { namespace :: [FieldName],- typename :: TypeName- }- deriving (Show)+data DERIVING_MODE = SCALAR_MODE | ENUM_MODE | TYPE_MODE -data ClientConstructorDefinition = ClientConstructorDefinition- { cName :: TypeName,- cFields :: [FieldDefinition ANY VALID]- }- deriving (Show)+data ClientDeclaration+ = ToJSONClass DERIVING_MODE CodeGenType+ | FromJSONClass DERIVING_MODE CodeGenType+ | RequestTypeClass RequestTypeDefinition+ | ClientType CodeGenType data ClientTypeDefinition = ClientTypeDefinition- { clientTypeName :: TypeNameTH,- clientCons :: [ClientConstructorDefinition],+ { clientTypeName :: CodeGenTypeName,+ clientCons :: [CodeGenConstructor], clientKind :: TypeKind } deriving (Show) -data FetchDefinition = FetchDefinition- { rootTypeName :: TypeNameTH,- clientArgumentsTypeName :: Maybe TypeNameTH,- fetchOperationType :: OperationType+data RequestTypeDefinition = RequestTypeDefinition+ { requestName :: TypeName,+ requestArgs :: TypeName,+ requestType :: OperationType,+ requestQuery :: String } deriving (Show)
src/Data/Morpheus/Client/Internal/Utils.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Client.Internal.Utils@@ -7,19 +8,27 @@ getSource, handleResult, getFile,+ omitNulls,+ emptyTypeError,+ takeValueType, ) where import Control.Monad.Except (MonadError (catchError))+import Data.Aeson (Object)+import Data.Aeson.Types (Pair, Parser, Value (..), object) import qualified Data.ByteString.Lazy.Char8 as L import Data.FileEmbed (makeRelativeToProject) import Data.List (isSuffixOf) import Data.Morpheus.Client.Internal.Types- ( ClientConstructorDefinition (cFields),- SchemaSource (..),+ ( SchemaSource (..), )+import Data.Morpheus.CodeGen.Internal.AST (CodeGenConstructor (..), CodeGenTypeName (..), getFullName) import Data.Morpheus.Error (gqlWarnings, renderGQLErrors) import Data.Morpheus.Internal.Ext (GQLResult, Result (..))+import Data.Morpheus.Internal.Utils (IsMap (lookup))+import Data.Morpheus.Types.Internal.AST (Msg (msg), internal)+import qualified Data.Text as T import qualified Data.Text.IO as TIO import Language.Haskell.TH (Q, runIO) import Language.Haskell.TH.Syntax (qAddDependentFile)@@ -37,8 +46,8 @@ | x `elem` collected = collectElems (collected, errors <> [x]) xs | otherwise = collectElems (collected <> [x], errors) xs -isEnum :: [ClientConstructorDefinition] -> Bool-isEnum = all (null . cFields)+isEnum :: [CodeGenConstructor] -> Bool+isEnum = all (null . constructorFields) getSource :: FilePath -> Q SchemaSource getSource p@@ -65,3 +74,20 @@ { result, warnings } -> gqlWarnings warnings >> f result++omitNulls :: [Pair] -> Value+omitNulls = object . filter notNull+ where+ notNull (_, Null) = False+ notNull _ = True++emptyTypeError :: MonadFail m => CodeGenTypeName -> m a+emptyTypeError name = fail $ show $ internal ("Type " <> msg (getFullName name) <> " Should Have at least one Constructor")++takeValueType :: ((String, Object) -> Parser a) -> Value -> Parser a+takeValueType f (Object hMap) = case lookup "__typename" hMap of+ Nothing -> fail "key \"__typename\" not found on object"+ Just (String x) -> f (T.unpack x, hMap)+ Just val ->+ fail $ "key \"__typename\" should be string but found: " <> show val+takeValueType _ _ = fail "expected Object"
+ src/Data/Morpheus/Client/Printing/TH.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.Client.Printing.TH+ ( printDeclarations,+ )+where++import Data.Aeson+ ( FromJSON (parseJSON),+ KeyValue ((.=)),+ ToJSON (toJSON),+ withObject,+ )+import Data.Morpheus.Client.Fetch.RequestType+ ( RequestType (..),+ )+import Data.Morpheus.Client.Internal.TH+ ( declareIfNotDeclared,+ decodeObjectE,+ deriveIfNotDefined,+ failExp,+ matchWith,+ mkFieldsE,+ )+import Data.Morpheus.Client.Internal.Types+ ( ClientDeclaration (..),+ DERIVING_MODE (..),+ RequestTypeDefinition (..),+ )+import Data.Morpheus.Client.Internal.Utils+ ( emptyTypeError,+ omitNulls,+ takeValueType,+ )+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenConstructor (..),+ CodeGenType (..),+ CodeGenTypeName (..),+ getFullName,+ )+import Data.Morpheus.CodeGen.TH+ ( PrintDec (printDec),+ ToString (..),+ destructConstructor,+ printTypeClass,+ toCon,+ toName,+ toString,+ toVar,+ v',+ _',+ )+import Data.Morpheus.Types.GQLScalar+ ( scalarFromJSON,+ scalarToJSON,+ )+import Data.Morpheus.Types.Internal.AST+ ( TypeName,+ )+import Language.Haskell.TH+ ( Dec,+ DecQ,+ Exp (..),+ ExpQ,+ PatQ,+ Q,+ appE,+ tupP,+ )+import Relude hiding (ToString, toString)++printDeclarations :: [ClientDeclaration] -> Q [Dec]+printDeclarations clientType = concat <$> traverse typeDeclarations clientType++typeDeclarations :: ClientDeclaration -> Q [Dec]+typeDeclarations (FromJSONClass mode clientDef) = deriveIfNotDefined (deriveFromJSON mode) ''FromJSON clientDef+typeDeclarations (ToJSONClass mode clientDef) = deriveIfNotDefined (deriveToJSON mode) ''ToJSON clientDef+typeDeclarations (ClientType c) = declareIfNotDeclared printDec c+typeDeclarations (RequestTypeClass RequestTypeDefinition {..}) =+ pure <$> printTypeClass [] ''RequestType (toCon requestName) [(''RequestArgs, toCon requestArgs)] methods+ where+ methods =+ [ ('__name, [_'], [|requestName|]),+ ('__query, [_'], [|requestQuery|]),+ ('__type, [_'], [|requestType|])+ ]++-- UTILS++mkFromJSON :: CodeGenTypeName -> ExpQ -> DecQ+mkFromJSON name expr = printTypeClass [] ''FromJSON (toCon (toName name)) [] [('parseJSON, [], expr)]++mkToJSON :: CodeGenTypeName -> [PatQ] -> ExpQ -> DecQ+mkToJSON name args expr = printTypeClass [] ''ToJSON (toCon $ toName name) [] [('toJSON, args, expr)]++originalLit :: ToString TypeName a => CodeGenTypeName -> Q a+originalLit = toString . typename++-- FromJSON+deriveFromJSON :: DERIVING_MODE -> CodeGenType -> DecQ+deriveFromJSON SCALAR_MODE CodeGenType {cgTypeName} = mkFromJSON cgTypeName [|scalarFromJSON|]+deriveFromJSON _ CodeGenType {cgConstructors = [], ..} = emptyTypeError cgTypeName+deriveFromJSON ENUM_MODE CodeGenType {..} =+ mkFromJSON cgTypeName (matchWith (Just (v', failExp)) fromJSONEnum cgConstructors)+deriveFromJSON _ CodeGenType {cgConstructors = [cons], ..} =+ mkFromJSON cgTypeName (fromJSONObject cons)+deriveFromJSON _ typeD@CodeGenType {cgTypeName} =+ mkFromJSON cgTypeName (fromJSONUnion typeD)++fromJSONEnum :: CodeGenConstructor -> (PatQ, ExpQ)+fromJSONEnum CodeGenConstructor {constructorName} = (originalLit constructorName, appE (toVar 'pure) (toCon constructorName))++fromJSONObject :: CodeGenConstructor -> ExpQ+fromJSONObject con@CodeGenConstructor {constructorName} = withBody <$> decodeObjectE con+ where+ withBody body = AppE (AppE (VarE 'withObject) name) (LamE [v'] body)+ name :: Exp+ name = toString (getFullName constructorName)++fromJSONUnion :: CodeGenType -> ExpQ+fromJSONUnion CodeGenType {..} = appE (toVar 'takeValueType) (matchWith elseCondition f cgConstructors)+ where+ elseCondition =+ (tupP [_', v'],) . decodeObjectE+ <$> find ((typename cgTypeName ==) . typename . constructorName) cgConstructors+ f cons@CodeGenConstructor {..} =+ ( tupP [originalLit constructorName, if null constructorFields then _' else v'],+ decodeObjectE cons+ )++-- ToJSON+deriveToJSON :: DERIVING_MODE -> CodeGenType -> DecQ+deriveToJSON SCALAR_MODE CodeGenType {..} = mkToJSON cgTypeName [] [|scalarToJSON|]+deriveToJSON _ CodeGenType {cgConstructors = [], ..} = emptyTypeError cgTypeName+deriveToJSON ENUM_MODE CodeGenType {..} =+ mkToJSON cgTypeName [] (matchWith Nothing toJSONEnum cgConstructors)+deriveToJSON _ CodeGenType {cgConstructors = [cons], ..} =+ mkToJSON cgTypeName [destructConstructor cons] (toJSONObject cons)+deriveToJSON _ _ = fail "Input Unions are not yet supported"++toJSONEnum :: CodeGenConstructor -> (PatQ, ExpQ)+toJSONEnum CodeGenConstructor {constructorName} = (toCon constructorName, originalLit constructorName)++toJSONObject :: CodeGenConstructor -> ExpQ+toJSONObject CodeGenConstructor {..} = pure $ AppE (VarE 'omitNulls) (mkFieldsE constructorName '(.=) constructorFields)
src/Data/Morpheus/Client/Transform/Core.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NoImplicitPrelude #-} @@ -13,13 +14,24 @@ getType, typeFrom, deprecationWarning,+ toCodeGenField,+ toClientDeclarations, ) where import Control.Monad.Except (MonadError)-import Data.Morpheus.CodeGen.Internal.TH- ( camelCaseTypeName,+import Data.Morpheus.Client.Internal.Types+ ( ClientDeclaration (..),+ ClientTypeDefinition (..),+ DERIVING_MODE (..), )+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenField (..),+ CodeGenType (..),+ DerivingClass (..),+ FIELD_TYPE_WRAPPER (..),+ )+import Data.Morpheus.CodeGen.Utils (camelCaseTypeName) import Data.Morpheus.Error ( deprecatedField, )@@ -33,6 +45,7 @@ import Data.Morpheus.Types.Internal.AST ( ANY, Directives,+ FieldDefinition (..), FieldName, GQLError, RAW,@@ -40,10 +53,14 @@ Schema (..), TypeContent (..), TypeDefinition (..),+ TypeKind (..), TypeName,+ TypeRef (..), VALID, VariableDefinitions, internal,+ isNullable,+ isResolverType, lookupDeprecated, lookupDeprecatedReason, msg,@@ -95,3 +112,29 @@ (lookupDeprecatedReason deprecation) ] Nothing -> pure ()++toCodeGenField :: FieldDefinition a b -> CodeGenField+toCodeGenField FieldDefinition {fieldType = field@TypeRef {..}, ..} =+ CodeGenField+ { fieldName,+ fieldType = typeConName,+ wrappers = [GQL_WRAPPER typeWrappers],+ fieldIsNullable = isNullable field+ }++toClientDeclarations :: ClientTypeDefinition -> [ClientDeclaration]+toClientDeclarations def@ClientTypeDefinition {clientKind}+ | KindScalar == clientKind = [FromJSONClass SCALAR_MODE cgType, ToJSONClass SCALAR_MODE cgType]+ | KindEnum == clientKind = [ClientType cgType, FromJSONClass ENUM_MODE cgType, ToJSONClass ENUM_MODE cgType]+ | isResolverType clientKind = [ClientType cgType, FromJSONClass TYPE_MODE cgType]+ | otherwise = [ClientType cgType, ToJSONClass TYPE_MODE cgType]+ where+ cgType = printClientType def++printClientType :: ClientTypeDefinition -> CodeGenType+printClientType ClientTypeDefinition {..} =+ CodeGenType+ { cgTypeName = clientTypeName,+ cgConstructors = clientCons,+ cgDerivations = [GENERIC, SHOW, CLASS_EQ]+ }
src/Data/Morpheus/Client/Transform/Global.hs view
@@ -11,10 +11,15 @@ where import Data.Morpheus.Client.Internal.Types- ( ClientConstructorDefinition (..),+ ( ClientDeclaration, ClientTypeDefinition (..),- TypeNameTH (..), )+import Data.Morpheus.Client.Transform.Core (toClientDeclarations, toCodeGenField)+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenConstructor (..),+ CodeGenTypeName (..),+ fromTypeName,+ ) import Data.Morpheus.Internal.Utils ( empty, )@@ -34,25 +39,24 @@ VariableDefinitions, isNotSystemTypeName, isResolverType,- toAny, ) import Relude hiding (empty) toArgumentsType ::- TypeName ->+ CodeGenTypeName -> VariableDefinitions RAW -> Maybe ClientTypeDefinition-toArgumentsType cName variables+toArgumentsType clientTypeName variables | null variables = Nothing | otherwise = Just ClientTypeDefinition- { clientTypeName = TypeNameTH [] cName,+ { clientTypeName, clientKind = KindInputObject, clientCons =- [ ClientConstructorDefinition- { cName,- cFields = toFieldDefinition <$> toList variables+ [ CodeGenConstructor+ { constructorName = clientTypeName,+ constructorFields = toCodeGenField . toFieldDefinition <$> toList variables } ] }@@ -67,10 +71,11 @@ fieldDirectives = empty } -toGlobalDefinitions :: (TypeName -> Bool) -> Schema VALID -> [ClientTypeDefinition]+toGlobalDefinitions :: (TypeName -> Bool) -> Schema VALID -> [ClientDeclaration] toGlobalDefinitions f Schema {types} =- mapMaybe generateGlobalType $- filter shouldInclude (toList types)+ concatMap toClientDeclarations $+ mapMaybe generateGlobalType $+ filter shouldInclude (toList types) where shouldInclude t = not (isResolverType t)@@ -82,24 +87,25 @@ (clientKind, clientCons) <- genContent typeContent pure ClientTypeDefinition- { clientTypeName = TypeNameTH [] typeName,+ { clientTypeName = fromTypeName typeName, clientKind, clientCons } where- genContent :: TypeContent TRUE ANY VALID -> Maybe (TypeKind, [ClientConstructorDefinition])- genContent (DataInputObject inputFields) = do+ genContent :: TypeContent TRUE ANY VALID -> Maybe (TypeKind, [CodeGenConstructor])+ genContent (DataInputObject inputFields) = pure ( KindInputObject,- [ ClientConstructorDefinition- { cName = typeName,- cFields = toAny <$> toList inputFields+ [ CodeGenConstructor+ { constructorName = fromTypeName typeName,+ constructorFields = toCodeGenField <$> toList inputFields } ] )- genContent (DataEnum enumTags) = pure (KindEnum, mkConsEnum <$> enumTags)+ genContent (DataEnum enumTags) = pure (KindEnum, mkConsEnum typeName <$> enumTags) genContent DataScalar {} = pure (KindScalar, []) genContent _ = Nothing -mkConsEnum :: DataEnumValue s -> ClientConstructorDefinition-mkConsEnum DataEnumValue {enumName} = ClientConstructorDefinition enumName []+mkConsEnum :: TypeName -> DataEnumValue s -> CodeGenConstructor+mkConsEnum typename DataEnumValue {enumName} =+ CodeGenConstructor (CodeGenTypeName [coerce typename] [] enumName) []
src/Data/Morpheus/Client/Transform/Local.hs view
@@ -14,13 +14,13 @@ import Control.Monad.Except (MonadError (throwError)) import Data.Morpheus.Client.Internal.Types- ( ClientConstructorDefinition (..),+ ( ClientDeclaration (..), ClientTypeDefinition (..),- FetchDefinition (..),- TypeNameTH (..),+ RequestTypeDefinition (..), )-import Data.Morpheus.Client.Transform.Core (Converter (..), compileError, deprecationWarning, getType, typeFrom)+import Data.Morpheus.Client.Transform.Core (Converter (..), compileError, deprecationWarning, getType, toClientDeclarations, toCodeGenField, typeFrom) import Data.Morpheus.Client.Transform.Global (toArgumentsType)+import Data.Morpheus.CodeGen.Internal.AST (CodeGenConstructor (..), CodeGenTypeName (..), fromTypeName) import Data.Morpheus.Core (Config (..), VALIDATION_MODE (WITHOUT_VARIABLES), validateRequest) import Data.Morpheus.Internal.Ext ( GQLResult,@@ -56,6 +56,7 @@ msg, toAny, )+import qualified Data.Text as T import Relude hiding (empty, show) import Prelude (show) @@ -66,24 +67,16 @@ validationMode = WITHOUT_VARIABLES } -toLocalDefinitions ::- ExecutableDocument ->- Schema VALID ->- GQLResult- ( FetchDefinition,- [ClientTypeDefinition]- )-toLocalDefinitions request schema = do+toLocalDefinitions :: (Text, ExecutableDocument) -> Schema VALID -> GQLResult [ClientDeclaration]+toLocalDefinitions (query, request) schema = do validOperation <- validateRequest clientConfig schema request- flip runReaderT (schema, operationArguments $ operation request) $- runConverter $- genOperation validOperation+ flip runReaderT (schema, operationArguments $ operation request) $ runConverter $ genLocalDeclarations query validOperation -genOperation :: Operation VALID -> Converter (FetchDefinition, [ClientTypeDefinition])-genOperation op@Operation {operationName, operationSelection, operationType} = do+genLocalDeclarations :: Text -> Operation VALID -> Converter [ClientDeclaration]+genLocalDeclarations query op@Operation {operationName, operationSelection, operationType} = do (schema, varDefs) <- asks id datatype <- getOperationDataType op schema- let argumentsType = toArgumentsType (getOperationName operationName <> "Args") varDefs+ let argumentsType = toArgumentsType (fromTypeName $ getOperationName operationName <> "Args") varDefs (rootType :| localTypes) <- genLocalTypes []@@ -91,12 +84,14 @@ (toAny datatype) operationSelection pure- ( FetchDefinition- { clientArgumentsTypeName = fmap clientTypeName argumentsType,- rootTypeName = clientTypeName rootType,- fetchOperationType = operationType- },- rootType : (localTypes <> maybeToList argumentsType)+ ( RequestTypeClass+ RequestTypeDefinition+ { requestArgs = maybe "()" (typename . clientTypeName) argumentsType,+ requestName = typename (clientTypeName rootType),+ requestType = operationType,+ requestQuery = T.unpack query+ }+ : concatMap toClientDeclarations (rootType : (localTypes <> maybeToList argumentsType)) ) -------------------------------------------------------------------------@@ -107,11 +102,12 @@ TypeDefinition ANY VALID -> SelectionSet VALID -> Converter (NonEmpty ClientTypeDefinition)-genLocalTypes path tName dataType recordSelSet = do- (con, subTypes) <- toConstructorDefinition (if null path then [coerce tName] else path) tName dataType recordSelSet+genLocalTypes namespace tName dataType recordSelSet = do+ let clientTypeName = CodeGenTypeName {namespace, typeParameters = [], typename = tName}+ (con, subTypes) <- toConstructorDefinition (if null namespace then [coerce tName] else namespace) clientTypeName dataType recordSelSet pure $ ClientTypeDefinition- { clientTypeName = TypeNameTH path tName,+ { clientTypeName, clientCons = [con], clientKind = KindObject Nothing }@@ -119,13 +115,13 @@ toConstructorDefinition :: [FieldName] ->- TypeName ->+ CodeGenTypeName -> TypeDefinition ANY VALID -> SelectionSet VALID ->- Converter (ClientConstructorDefinition, [ClientTypeDefinition])-toConstructorDefinition path cName datatype selSet = do- (cFields, subTypes) <- unzip <$> traverse genField (toList selSet)- pure (ClientConstructorDefinition {cName, cFields}, concat subTypes)+ Converter (CodeGenConstructor, [ClientTypeDefinition])+toConstructorDefinition path name datatype selSet = do+ (fields, subTypes) <- unzip <$> traverse genField (toList selSet)+ pure (CodeGenConstructor {constructorName = name, constructorFields = map toCodeGenField fields}, concat subTypes) where genField :: Selection VALID -> Converter (FieldDefinition ANY VALID, [ClientTypeDefinition]) genField sel = do@@ -151,25 +147,25 @@ Selection VALID -> Converter [ClientTypeDefinition] subTypesBySelection _ _ Selection {selectionContent = SelectionField} = pure []-subTypesBySelection path dType Selection {selectionContent = SelectionSet selectionSet} = do+subTypesBySelection path dType Selection {selectionContent = SelectionSet selectionSet} = toList <$> genLocalTypes path (typeFrom [] dType) dType selectionSet-subTypesBySelection path dType Selection {selectionContent = UnionSelection interface unionSelections} =+subTypesBySelection namespace dType Selection {selectionContent = UnionSelection interface unionSelections} = do let variants = UnionTag (typeName dType) interface : toList unionSelections- (clientCons, subTypes) <- unzip <$> traverse (getVariantType path) variants+ (clientCons, subTypes) <- unzip <$> traverse (getVariantType namespace) variants pure ( ClientTypeDefinition- { clientTypeName = TypeNameTH path (typeFrom [] dType),+ { clientTypeName = CodeGenTypeName {namespace, typeParameters = [], typename = typeFrom [] dType}, clientCons, clientKind = KindUnion } : concat subTypes ) -getVariantType :: [FieldName] -> UnionTag -> Converter (ClientConstructorDefinition, [ClientTypeDefinition])+getVariantType :: [FieldName] -> UnionTag -> Converter (CodeGenConstructor, [ClientTypeDefinition]) getVariantType path (UnionTag selectedTyName selectionVariant) = do conDatatype <- getType selectedTyName- toConstructorDefinition path selectedTyName conDatatype selectionVariant+ toConstructorDefinition path (CodeGenTypeName path [] selectedTyName) conDatatype selectionVariant getFieldType :: [FieldName] ->