packages feed

graphql-client 1.1.1 → 1.2.0

raw patch · 18 files changed

+433/−332 lines, 18 filesdep ~basedep ~template-haskellPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, template-haskell

API changes (from Hackage documentation)

- Data.GraphQL.Monad: instance GHC.Base.Monad m => Control.Monad.Reader.Class.MonadReader Data.GraphQL.Monad.QueryState (Data.GraphQL.Monad.GraphQLQueryT m)
- Data.GraphQL.Query: type family ResultSchema query :: Schema;
+ Data.GraphQL.Monad: class Monad m => MonadGraphQLQuery m
+ Data.GraphQL.Monad: data GraphQLManager
+ Data.GraphQL.Monad: initGraphQLManager :: GraphQLSettings -> IO GraphQLManager
+ Data.GraphQL.Monad: runQuery :: (MonadIO m, MonadGraphQLQuery m, GraphQLQuery query, schema ~ ResultSchema query) => query -> m (Object schema)
+ Data.GraphQL.Monad: runQuerySafe :: (MonadGraphQLQuery m, GraphQLQuery query, schema ~ ResultSchema query) => query -> m (GraphQLResult (Object schema))
+ Data.GraphQL.Monad: runQuerySafeIO :: (GraphQLQuery query, schema ~ ResultSchema query) => GraphQLManager -> query -> IO (GraphQLResult (Object schema))
+ Data.GraphQL.Query: type ResultSchema query :: Schema;
- Data.GraphQL.Bootstrap: (.=) :: (KeyValue kv, ToJSON v) => Text -> v -> kv
+ Data.GraphQL.Bootstrap: (.=) :: (KeyValue kv, ToJSON v) => Key -> v -> kv

Files

CHANGELOG.md view
@@ -1,12 +1,20 @@-## Upcoming+# v1.2.0 -## 1.1.1+Breaking changes: +* Remove support for GHC < 8.10++New features:++* Added `runQuerySafeIO` and expose `GraphQLManager` for applications that want to manually implement `MonadGraphQLQuery`++# v1.1.1+ Bug fixes:  * Generate enums that only appear in query arguments ([#59](https://github.com/LeapYear/graphql-client/pull/59)) -## 1.1.0+# v1.1.0  Breaking changes: @@ -22,7 +30,7 @@     * Add `Show` instance for `AnyResultMock`     * Add `MonadTrans` instance for `MockQueryT` -## 1.0.0+# v1.0.0  Initial release: 
README.md view
@@ -1,8 +1,8 @@ # graphql-client -![CircleCI](https://img.shields.io/circleci/build/github/LeapYear/graphql-client)-![Hackage](https://img.shields.io/hackage/v/graphql-client)-[![codecov](https://codecov.io/gh/LeapYear/graphql-client/branch/master/graph/badge.svg?token=WIOxotqPTN)](https://codecov.io/gh/LeapYear/graphql-client)+[![GitHub Actions](https://img.shields.io/github/workflow/status/LeapYear/graphql-client/CI/main)](https://github.com/LeapYear/graphql-client/actions?query=branch%3Amain)+[![codecov](https://codecov.io/gh/LeapYear/graphql-client/branch/main/graph/badge.svg?token=WIOxotqPTN)](https://codecov.io/gh/LeapYear/graphql-client)+[![Hackage](https://img.shields.io/hackage/v/graphql-client)](https://hackage.haskell.org/package/graphql-client)  A client for Haskell applications to query [GraphQL](https://graphql.org) APIs. This package provides two resources: 
exe/Codegen.hs view
@@ -15,56 +15,68 @@ import Options.Applicative import Path import Path.IO (doesFileExist, ensureDir, resolveFile', withSystemTempDir)-import System.Exit (ExitCode(..))+import System.Exit (ExitCode (..)) import System.Process.Typed (proc, readProcess, runProcess_)  data CliOptions = CliOptions-  { cliNode   :: Maybe FilePath+  { cliNode :: Maybe FilePath   , cliConfig :: FilePath   }  parseCliOptions :: Parser CliOptions-parseCliOptions = CliOptions-  <$> parseCliNode-  <*> parseCliConfig+parseCliOptions =+  CliOptions+    <$> parseCliNode+    <*> parseCliConfig   where-    parseCliNode = optional $ strOption $ mconcat-      [ long "node"-      , metavar "NODE"-      , help "Path to Node.JS executable (default to finding on PATH)"-      ]-    parseCliConfig = strOption $ mconcat-      [ long "config"-      , short 'c'-      , metavar "CONFIG"-      , help "Path to codegen.yml file (defaults to ./codegen.yml)"-      , value "./codegen.yml"-      ]+    parseCliNode =+      optional $+        strOption $+          mconcat+            [ long "node"+            , metavar "NODE"+            , help "Path to Node.JS executable (default to finding on PATH)"+            ]+    parseCliConfig =+      strOption $+        mconcat+          [ long "config"+          , short 'c'+          , metavar "CONFIG"+          , help "Path to codegen.yml file (defaults to ./codegen.yml)"+          , value "./codegen.yml"+          ]  graphqlCodegenScript :: ByteString-graphqlCodegenScript = $(do-  let script = [relfile|js/graphql-codegen-haskell.js|]-      fallbackScript = [relfile|js/graphql-codegen-haskell-fallback.js|]+graphqlCodegenScript =+  $( do+      let script = [relfile|js/graphql-codegen-haskell.js|]+          fallbackScript = [relfile|js/graphql-codegen-haskell-fallback.js|] -  scriptExists <- runIO $ doesFileExist script-  embedFile $ toFilePath $ if scriptExists then script else fallbackScript-  )+      scriptExists <- runIO $ doesFileExist script+      embedFile $ toFilePath $ if scriptExists then script else fallbackScript+   )  mockedLibraries :: [(Path Rel File, ByteString)]-mockedLibraries = $(do-  let dir = "js/mocks/"-  files <- runIO $ getDir dir-  listE $ flip map files $ \(fp, content) -> do-    addDependentFile $ dir ++ fp-    tupE [mkRelFile fp, bsToExp content]-  )+mockedLibraries =+  $( do+      let dir = "js/mocks/"+      files <- runIO $ getDir dir+      listE $+        flip map files $ \(fp, content) -> do+          addDependentFile $ dir ++ fp+          tupE [mkRelFile fp, bsToExp content]+   )  main :: IO () main = do-  CliOptions{..} <- execParser $ info (parseCliOptions <**> helper) $ mconcat-    [ fullDesc-    , progDesc "Generate Haskell definitions from .graphql files"-    ]+  CliOptions{..} <-+    execParser $+      info (parseCliOptions <**> helper) $+        mconcat+          [ fullDesc+          , progDesc "Generate Haskell definitions from .graphql files"+          ]    withSystemTempDir "graphql-codegen" $ \tmpDir -> do     let graphqlCodegen = tmpDir </> [relfile|graphql-codegen-haskell.js|]@@ -80,7 +92,8 @@      configFileExists <- doesFileExist configFile     unless configFileExists $-      errorWithoutStackTrace $ "Config file doesn't exist: " ++ toFilePath configFile+      errorWithoutStackTrace $+        "Config file doesn't exist: " ++ toFilePath configFile      try @SomeException (readProcess $ proc nodeExe ["-e", "console.log('TEST')"]) >>= \case       Right (ExitSuccess, "TEST\n", _) -> return ()
graphql-client.cabal view
@@ -1,13 +1,11 @@ cabal-version: >= 1.10 --- This file has been generated from package.yaml by hpack version 0.33.0.+-- This file has been generated from package.yaml by hpack version 0.35.0. -- -- see: https://github.com/sol/hpack------ hash: c25accf7e3c21d0afe69d04182d67865d8683906b4a860ad622fb99dcfcaa63d  name:           graphql-client-version:        1.1.1+version:        1.2.0 synopsis:       A client for Haskell programs to query a GraphQL API description:    A client for Haskell programs to query a GraphQL API. category:       Graphql@@ -46,22 +44,22 @@       src   ghc-options: -Wall   build-depends:-      aeson >=1.2.4.0 && <1.6-    , aeson-schemas >=1.3 && <1.4-    , base >=4.9 && <5+      aeson >=1.2.4.0 && <3+    , aeson-schemas >=1.3 && <1.5+    , base >=4.14 && <5     , http-client >=0.5.13.1 && <0.8     , http-client-tls >=0.3.5.3 && <0.4     , http-types >=0.12.1 && <0.13     , mtl >=2.2.2 && <2.3-    , template-haskell >=2.12.0.0 && <3+    , template-haskell >=2.16 && <3     , text >=1.2.3.0 && <1.3     , transformers >=0.5.2.0 && <0.6     , unliftio-core >=0.1.1.0 && <0.3+  default-language: Haskell2010   if impl(ghc >= 8.0)     ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances   if impl(ghc < 8.8)     ghc-options: -Wnoncanonical-monadfail-instances-  default-language: Haskell2010  executable graphql-codegen   main-is: exe/Codegen.hs@@ -69,29 +67,29 @@       Paths_graphql_client   ghc-options: -Wall   build-depends:-      aeson >=1.2.4.0 && <1.6-    , aeson-schemas >=1.3 && <1.4-    , base >=4.9 && <5-    , bytestring >=0.10.8.2 && <0.11-    , file-embed >=0.0.10.1 && <0.0.14+      aeson >=1.2.4.0 && <3+    , aeson-schemas >=1.3 && <1.5+    , base >=4.14 && <5+    , bytestring >=0.10.8.2 && <0.12+    , file-embed >=0.0.10.1 && <0.1     , graphql-client     , http-client >=0.5.13.1 && <0.8     , http-client-tls >=0.3.5.3 && <0.4     , http-types >=0.12.1 && <0.13     , mtl >=2.2.2 && <2.3-    , optparse-applicative >=0.14.2.0 && <0.16.2-    , path >=0.6.1 && <0.8.0-    , path-io >=1.3.3 && <1.7.0-    , template-haskell >=2.12.0.0 && <3+    , optparse-applicative >=0.14.2.0 && <0.18+    , path >=0.6.1 && <0.10+    , path-io >=1.3.3 && <1.8+    , template-haskell >=2.16 && <3     , text >=1.2.3.0 && <1.3     , transformers >=0.5.2.0 && <0.6-    , typed-process >=0.2.3.0 && <0.2.7+    , typed-process >=0.2.3.0 && <0.3     , unliftio-core >=0.1.1.0 && <0.3+  default-language: Haskell2010   if impl(ghc >= 8.0)     ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances   if impl(ghc < 8.8)     ghc-options: -Wnoncanonical-monadfail-instances-  default-language: Haskell2010  test-suite graphql-client-test   type: exitcode-stdio-1.0@@ -105,9 +103,9 @@       test   ghc-options: -Wall   build-depends:-      aeson >=1.2.4.0 && <1.6-    , aeson-schemas >=1.3 && <1.4-    , base >=4.9 && <5+      aeson >=1.2.4.0 && <3+    , aeson-schemas >=1.3 && <1.5+    , base >=4.14 && <5     , graphql-client     , http-client >=0.5.13.1 && <0.8     , http-client-tls >=0.3.5.3 && <0.4@@ -115,12 +113,12 @@     , mtl >=2.2.2 && <2.3     , tasty     , tasty-hunit-    , template-haskell >=2.12.0.0 && <3+    , template-haskell >=2.16 && <3     , text >=1.2.3.0 && <1.3     , transformers >=0.5.2.0 && <0.6     , unliftio-core >=0.1.1.0 && <0.3+  default-language: Haskell2010   if impl(ghc >= 8.0)     ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances   if impl(ghc < 8.8)     ghc-options: -Wnoncanonical-monadfail-instances-  default-language: Haskell2010
js/graphql-codegen-haskell-fallback.js view
@@ -21,12 +21,12 @@ the GRAPHQL_CODEGEN environment variable to the location of that bundle. You can get the bundle in one of the following ways: -  1. Download from CircleCI (recommended)+  1. Download from CI (recommended)       a. Go to ${utils.GITHUB_URL}       b. Navigate to the appropriate commit-      c. Click on the commit status icon and click on the 'test_latest' CI job-      d. Go to the Artifacts tab-      e. Download 'graphql-codegen-haskell.js'+      c. Click on the commit status icon and click on the any of the CI links+      d. Click on the "Summary" tab+      e. Under "Artifacts", download 'graphql-codegen-haskell.js'   2. Build manually       a. Clone ${utils.GITHUB_URL}       b. Follow the instructions in DEVELOPER.md to build the bundle
js/graphql-codegen-haskell.js view
@@ -74,21 +74,7 @@ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 PERFORMANCE OF THIS SOFTWARE.
 ***************************************************************************** */
-/* global Reflect, Promise */
 
-var extendStatics = function(d, b) {
-    extendStatics = Object.setPrototypeOf ||
-        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
-        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
-    return extendStatics(d, b);
-};
-
-function __extends(d, b) {
-    extendStatics(d, b);
-    function __() { this.constructor = d; }
-    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
-}
-
 var __assign = function() {
     __assign = Object.assign || function __assign(t) {
         for (var s, i = 1, n = arguments.length; i < n; i++) {
@@ -138,6 +124,7 @@     }
 }
 
+/** @deprecated */
 function __spreadArrays() {
     for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
     for (var r = Array(s), k = 0, i = 0; i < il; i++)
@@ -15020,6 +15007,35 @@     findDeprecatedUsages: findDeprecatedUsages }); +/*! *****************************************************************************
+Copyright (c) Microsoft Corporation.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+***************************************************************************** */
+/* global Reflect, Promise */
+
+var extendStatics = function(d, b) {
+    extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+    return extendStatics(d, b);
+};
+
+function __extends(d, b) {
+    extendStatics(d, b);
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+}+ var cleanInternalStack = function (stack) { return stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); };  /**
@@ -53360,7 +53376,7 @@ }))); }); -var template = "{- This file was automatically generated and should not be edited. -}\n\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE QuasiQuotes #-}\n{-# LANGUAGE TypeFamilies #-}\n{-# OPTIONS_GHC -w #-}\n\nmodule {{apiModule}} where\n\nimport Data.GraphQL\nimport Data.GraphQL.Bootstrap\n\nimport {{scalarsModule}}\n{{#enumModules}}\nimport {{.}}\n{{/enumModules}}\n\n{{#operations}}\n{-----------------------------------------------------------------------------\n* {{name}}\n\n-- result :: Object {{schemaType}}; throws a GraphQL exception on errors\nresult <- runQuery {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} = ...\n  {{/overArgs}}\n  }\n\n-- result :: GraphQLResult (Object {{schemaType}})\nresult <- runQuerySafe {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} = ...\n  {{/overArgs}}\n  }\n-----------------------------------------------------------------------------}\n\ndata {{queryName}} = {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} :: {{type}}\n  {{/overArgs}}\n  }\n  deriving (Show)\n\ntype {{schemaType}} = [schema|\n  {{schema}}\n|]\n\ninstance GraphQLQuery {{queryName}} where\n  type ResultSchema {{queryName}} = {{schemaType}}\n\n  getQueryName _ = \"{{name}}\"\n\n  getQueryText _ = [query|\n    {{queryText}}\n  |]\n\n  getArgs query = object\n    {{#overArgs}}\n    [ \"{{arg}}\" .= _{{arg}} (query :: {{queryName}})\n    {{/overArgs}}\n    ]\n\n{{/operations}}\n";+var template = "{- This file was automatically generated and should not be edited. -}\n\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE DuplicateRecordFields #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE QuasiQuotes #-}\n{-# LANGUAGE TypeFamilies #-}\n{-# OPTIONS_GHC -w #-}\n\nmodule {{apiModule}} where\n\nimport Data.GraphQL\nimport Data.GraphQL.Bootstrap\n\nimport {{scalarsModule}}\n{{#enumModules}}\nimport {{.}}\n{{/enumModules}}\n\n{{#operations}}\n{-----------------------------------------------------------------------------\n-- {{name}}\n\n-- result :: Object {{schemaType}}; throws a GraphQL exception on errors\nresult <- runQuery {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} = ...\n  {{/overArgs}}\n  }\n\n-- result :: GraphQLResult (Object {{schemaType}})\nresult <- runQuerySafe {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} = ...\n  {{/overArgs}}\n  }\n-----------------------------------------------------------------------------}\n\ndata {{queryName}} = {{queryName}}\n  {{#overArgs}}\n  { _{{arg}} :: {{type}}\n  {{/overArgs}}\n  }\n  deriving (Show)\n\ntype {{schemaType}} = [schema|\n  {{schema}}\n|]\n\ninstance GraphQLQuery {{queryName}} where\n  type ResultSchema {{queryName}} = {{schemaType}}\n\n  getQueryName _ = \"{{name}}\"\n\n  getQueryText _ = [query|\n    {{queryText}}\n  |]\n\n  getArgs query = object\n    {{#overArgs}}\n    [ \"{{arg}}\" .= _{{arg}} (query :: {{queryName}})\n    {{/overArgs}}\n    ]\n\n{{/operations}}\n";  var renderAPIModule = function (config, enumModules, operations) {
     return mustache.render(template, __assign(__assign({}, config), { enumModules: enumModules, operations: operations.map(function (operation) { return (__assign(__assign({}, operation), { queryText: indent$3(indent$3(operation.queryText)), schema: indent$3(renderAesonSchema(operation.schema)), args: operation.args.map(function (_a) {
src/Data/GraphQL.hs view
@@ -1,4 +1,4 @@-{-|+{- | Module      :  Data.GraphQL Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,7 +6,6 @@  Core functionality for querying GraphQL APIs. -}- module Data.GraphQL (module X) where  import Data.Aeson.Schema as X
src/Data/GraphQL/Bootstrap.hs view
@@ -1,4 +1,4 @@-{-|+{- | Module      :  Data.GraphQL.Bootstrap Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,10 +6,9 @@  Imports needed for the generated API. -}--module Data.GraphQL.Bootstrap-  ( module X-  ) where+module Data.GraphQL.Bootstrap (+  module X,+) where  import Control.Monad.IO.Class as X (MonadIO) import Data.Aeson as X (object, (.=))
src/Data/GraphQL/Error.hs view
@@ -1,4 +1,8 @@-{-|+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}++{- | Module      :  Data.GraphQL.Error Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,33 +10,31 @@  Definitions for GraphQL errors and exceptions. -}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.GraphQL.Error-  ( GraphQLError(..)-  , GraphQLErrorLoc(..)-  , GraphQLException(..)-  ) where+module Data.GraphQL.Error (+  GraphQLError (..),+  GraphQLErrorLoc (..),+  GraphQLException (..),+) where  import Control.Exception (Exception)-import Data.Aeson (FromJSON(..), ToJSON, Value, withObject, (.:))+import Data.Aeson (FromJSON (..), ToJSON, Value, withObject, (.:)) import Data.Text (Text) import GHC.Generics (Generic)  -- | An error in a GraphQL query. data GraphQLError = GraphQLError-  { message   :: Text+  { message :: Text   , locations :: Maybe [GraphQLErrorLoc]-  , path      :: Maybe [Value]-  } deriving (Show,Eq,Generic,ToJSON,FromJSON)+  , path :: Maybe [Value]+  }+  deriving (Show, Eq, Generic, ToJSON, FromJSON)  -- | A location in an error in a GraphQL query. data GraphQLErrorLoc = GraphQLErrorLoc   { errorLine :: Int-  , errorCol  :: Int-  } deriving (Show,Eq,Generic,ToJSON)+  , errorCol :: Int+  }+  deriving (Show, Eq, Generic, ToJSON)  instance FromJSON GraphQLErrorLoc where   parseJSON = withObject "GraphQLErrorLoc" $ \o ->@@ -42,4 +44,4 @@  -- | An exception thrown as a result of an error in a GraphQL query. newtype GraphQLException = GraphQLException [GraphQLError]-  deriving (Show,Exception,Eq)+  deriving (Show, Exception, Eq)
src/Data/GraphQL/Monad.hs view
@@ -1,77 +1,154 @@-{-|+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}++{- | Module      :  Data.GraphQL.Monad Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental Portability :  portable -Defines the 'GraphQLQueryT' monad transformer, which implements-'MonadGraphQLQuery' to allow querying GraphQL APIs.+Defines the 'MonadGraphQLQuery' type class to query GraphQL APIs.+Also provides the 'GraphQLQueryT' monad transformer that can be+added to a transformer stack to implement the type class, and the+'runQuerySafeIO' function to manually implement it yourself. -}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RecordWildCards #-}+module Data.GraphQL.Monad (+  -- * MonadGraphQLQuery API+  MonadGraphQLQuery (..),+  runQuery,+  runQuerySafeIO, -module Data.GraphQL.Monad-  ( module Data.GraphQL.Monad.Class-  , GraphQLQueryT-  , runGraphQLQueryT-  , GraphQLSettings(..)-  , defaultGraphQLSettings-  ) where+  -- * GraphQLSettings+  GraphQLSettings (..),+  defaultGraphQLSettings, -import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad.IO.Unlift (MonadUnliftIO(..))-import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT)+  -- * GraphQLManager+  GraphQLManager,+  initGraphQLManager,++  -- * GraphQLQueryT+  GraphQLQueryT,+  runGraphQLQueryT,+) where++import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.IO.Unlift (MonadUnliftIO (..))+import Control.Monad.Reader (ReaderT, ask, runReaderT) import Control.Monad.Trans.Class (MonadTrans) import Data.Aeson ((.=)) import qualified Data.Aeson as Aeson-import Network.HTTP.Client-    ( Manager-    , ManagerSettings-    , Request(..)-    , RequestBody(..)-    , httpLbs-    , newManager-    , parseUrlThrow-    , responseBody-    )+import Data.Aeson.Schema (Object)+import Network.HTTP.Client (+  Manager,+  ManagerSettings,+  Request (..),+  RequestBody (..),+  httpLbs,+  newManager,+  parseUrlThrow,+  responseBody,+ ) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types (hContentType)  import Data.GraphQL.Monad.Class-import Data.GraphQL.Query (GraphQLQuery(..))+import Data.GraphQL.Query (GraphQLQuery (..))+import Data.GraphQL.Result (GraphQLResult) --- | The state for running GraphQLQueryT.-data QueryState = QueryState+{- GraphQLSettings -}++-- | The settings for initializing a 'GraphQLManager'.+data GraphQLSettings = GraphQLSettings+  { managerSettings :: ManagerSettings+  -- ^ Uses TLS by default+  , url :: String+  , modifyReq :: Request -> Request+  }++{- | Default settings for 'GraphQLSettings'. Requires 'url' field to be overridden.++ Example usage:++ >>> defaultGraphQLSettings+ ...   { url = "https://api.github.com/graphql"+ ...   , modifyReq = \\req -> req+ ...       { requestHeaders =+ ...           (hAuthorization, "bearer my_github_token") : requestHeaders req+ ...       }+ ...   }+-}+defaultGraphQLSettings :: GraphQLSettings+defaultGraphQLSettings =+  GraphQLSettings+    { managerSettings = tlsManagerSettings+    , url = error "No URL is provided"+    , modifyReq = id+    }++{- The base runQuerySafeIO implementation -}++-- | The manager for running GraphQL queries.+data GraphQLManager = GraphQLManager   { manager :: Manager   , baseReq :: Request   } --- | The monad transformer type that should be used to run GraphQL queries.------ @--- newtype MyMonad a = MyMonad { unMyMonad :: GraphQLQueryT IO a }------ runMyMonad :: MyMonad a -> IO a--- runMyMonad = runGraphQLQueryT graphQLSettings . unMyMonad---   where---     graphQLSettings = defaultGraphQLSettings---       { url = "https://api.github.com/graphql"---       , modifyReq = \\req -> req---           { requestHeaders =---               (hAuthorization, "bearer my_github_token") : requestHeaders req---           }---       }--- @-newtype GraphQLQueryT m a = GraphQLQueryT { unGraphQLQueryT :: ReaderT QueryState m a }+initGraphQLManager :: GraphQLSettings -> IO GraphQLManager+initGraphQLManager GraphQLSettings{..} = do+  manager <- newManager managerSettings+  baseReq <- modifyReq . modifyReq' <$> parseUrlThrow url+  return GraphQLManager{..}+  where+    modifyReq' req =+      req+        { method = "POST"+        , requestHeaders = (hContentType, "application/json") : requestHeaders req+        }++-- | Execute a GraphQL query with the given 'GraphQLManager'.+runQuerySafeIO ::+  (GraphQLQuery query, schema ~ ResultSchema query) =>+  GraphQLManager ->+  query ->+  IO (GraphQLResult (Object schema))+runQuerySafeIO GraphQLManager{..} query = httpLbs request manager >>= decodeBody+  where+    request =+      baseReq+        { requestBody =+            RequestBodyLBS $+              Aeson.encode $+                Aeson.object+                  [ "query" .= getQueryText query+                  , "variables" .= getArgs query+                  ]+        }++    decodeBody = either fail return . Aeson.eitherDecode . responseBody++{- GraphQLQueryT monad transformer -}++{- | The monad transformer type that can be used to run GraphQL queries.++ @+ newtype MyMonad a = MyMonad { unMyMonad :: GraphQLQueryT IO a }++ runMyMonad :: MyMonad a -> IO a+ runMyMonad = runGraphQLQueryT graphQLSettings . unMyMonad+   where+     graphQLSettings = defaultGraphQLSettings{url = "https://api.github.com/graphql"}+ @+-}+newtype GraphQLQueryT m a = GraphQLQueryT {unGraphQLQueryT :: ReaderT GraphQLManager m a}   deriving     ( Functor     , Applicative     , Monad     , MonadIO-    , MonadReader QueryState     , MonadTrans     ) @@ -80,46 +157,11 @@  instance MonadIO m => MonadGraphQLQuery (GraphQLQueryT m) where   runQuerySafe query = do-    QueryState{..} <- ask--    let request = baseReq-          { requestBody = RequestBodyLBS $ Aeson.encode $ Aeson.object-              [ "query" .= getQueryText query-              , "variables" .= getArgs query-              ]-          }--    liftIO $ either fail return . Aeson.eitherDecode . responseBody =<< httpLbs request manager+    manager <- GraphQLQueryT ask+    liftIO $ runQuerySafeIO manager query --- | Run a GraphQLQueryT stack.+-- | Run the GraphQLQueryT monad transformer. runGraphQLQueryT :: MonadIO m => GraphQLSettings -> GraphQLQueryT m a -> m a-runGraphQLQueryT GraphQLSettings{..} m = do-  state <- liftIO $ do-    manager <- newManager managerSettings-    baseReq <- modifyReq . modifyReq' <$> parseUrlThrow url-    return QueryState{..}--  (`runReaderT` state)-    . unGraphQLQueryT-    $ m-  where-    modifyReq' req = req-      { method = "POST"-      , requestHeaders = (hContentType, "application/json") : requestHeaders req-      }---- | The settings for running GraphQLQueryT.-data GraphQLSettings = GraphQLSettings-  { managerSettings :: ManagerSettings-    -- ^ Uses TLS by default-  , url             :: String-  , modifyReq       :: Request -> Request-  }---- | Default query settings.-defaultGraphQLSettings :: GraphQLSettings-defaultGraphQLSettings = GraphQLSettings-  { managerSettings = tlsManagerSettings-  , url = error "No URL is provided"-  , modifyReq = id-  }+runGraphQLQueryT settings m = do+  manager <- liftIO $ initGraphQLManager settings+  (`runReaderT` manager) . unGraphQLQueryT $ m
src/Data/GraphQL/Monad/Class.hs view
@@ -1,4 +1,10 @@-{-|+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{- | Module      :  Data.GraphQL.Monad.Class Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,26 +12,20 @@  Defines the 'MonadGraphQLQuery' type class, which defines how GraphQL queries should be run. -}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}--module Data.GraphQL.Monad.Class-  ( MonadGraphQLQuery(..)-  , runQuery-  ) where+module Data.GraphQL.Monad.Class (+  MonadGraphQLQuery (..),+  runQuery,+) where  import Control.Exception (throwIO)-import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (ExceptT) import Control.Monad.Trans.Identity (IdentityT) import Control.Monad.Trans.Maybe (MaybeT)-import Control.Monad.Trans.Reader (ReaderT) import qualified Control.Monad.Trans.RWS.Lazy as Lazy import qualified Control.Monad.Trans.RWS.Strict as Strict+import Control.Monad.Trans.Reader (ReaderT) import qualified Control.Monad.Trans.State.Lazy as Lazy import qualified Control.Monad.Trans.State.Strict as Strict import qualified Control.Monad.Trans.Writer.Lazy as Lazy@@ -33,20 +33,23 @@ import Data.Aeson.Schema (Object) import Data.Maybe (fromJust) -import Data.GraphQL.Error (GraphQLException(..))-import Data.GraphQL.Query (GraphQLQuery(..))+import Data.GraphQL.Error (GraphQLException (..))+import Data.GraphQL.Query (GraphQLQuery (..)) import Data.GraphQL.Result (GraphQLResult, getErrors, getResult)  -- | A type class for monads that can run GraphQL queries. class Monad m => MonadGraphQLQuery m where-  runQuerySafe-    :: (GraphQLQuery query, schema ~ ResultSchema query)-    => query -> m (GraphQLResult (Object schema))+  -- | Run the given query and return the 'GraphQLResult'.+  runQuerySafe ::+    (GraphQLQuery query, schema ~ ResultSchema query) =>+    query ->+    m (GraphQLResult (Object schema)) --- | Runs the given query and returns the result, erroring if the query returned errors.-runQuery-  :: (MonadIO m, MonadGraphQLQuery m, GraphQLQuery query, schema ~ ResultSchema query)-  => query -> m (Object schema)+-- | Run the given query and returns the result, erroring if the query returned errors.+runQuery ::+  (MonadIO m, MonadGraphQLQuery m, GraphQLQuery query, schema ~ ResultSchema query) =>+  query ->+  m (Object schema) runQuery query = do   result <- runQuerySafe query   case getErrors result of
src/Data/GraphQL/Query.hs view
@@ -1,4 +1,9 @@-{-|+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeInType #-}++{- | Module      :  Data.GraphQL.Query Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,43 +11,41 @@  Definitions needed by GraphQL queries. -}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeInType #-}--module Data.GraphQL.Query-  ( GraphQLQuery(..)-  , query-  ) where+module Data.GraphQL.Query (+  GraphQLQuery (..),+  query,+) where  import Data.Aeson (Value) import Data.Aeson.Schema (IsSchema, Schema) import Data.Text (Text) import qualified Data.Text as Text-import Language.Haskell.TH.Quote (QuasiQuoter(..))+import Language.Haskell.TH.Quote (QuasiQuoter (..)) import Language.Haskell.TH.Syntax (lift) --- | A type class for defining GraphQL queries.------ Should be generated via the `graphql-codegen` command. Any manual instances needs--- to be certain that `getArgs query` satisfies the arguments defined in--- `getQueryText query`, and that the result adheres to `ResultSchema query`.+{- | A type class for defining GraphQL queries.++ Should be generated via the `graphql-codegen` command. Any manual instances needs+ to be certain that `getArgs query` satisfies the arguments defined in+ `getQueryText query`, and that the result adheres to `ResultSchema query`.+-} class IsSchema (ResultSchema query) => GraphQLQuery query where   type ResultSchema query :: Schema   getQueryName :: query -> Text   getQueryText :: query -> Text   getArgs :: query -> Value --- | A quasiquoter that interpolates the given string as raw text.------ Trying to avoid a dependency on raw-strings-qq+{- | A quasiquoter that interpolates the given string as raw text.++ Trying to avoid a dependency on raw-strings-qq+-} query :: QuasiQuoter-query = QuasiQuoter-  { quoteExp = liftText . Text.strip . Text.pack-  , quotePat = error "Cannot use the 'query' QuasiQuoter for patterns"-  , quoteType = error "Cannot use the 'query' QuasiQuoter for types"-  , quoteDec = error "Cannot use the 'query' QuasiQuoter for declarations"-  }+query =+  QuasiQuoter+    { quoteExp = liftText . Text.strip . Text.pack+    , quotePat = error "Cannot use the 'query' QuasiQuoter for patterns"+    , quoteType = error "Cannot use the 'query' QuasiQuoter for types"+    , quoteDec = error "Cannot use the 'query' QuasiQuoter for declarations"+    }   where-    liftText s = [| Text.pack $(lift $ Text.unpack s) |]+    liftText s = [|Text.pack $(lift $ Text.unpack s)|]
src/Data/GraphQL/Result.hs view
@@ -1,4 +1,8 @@-{-|+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}++{- | Module      :  Data.GraphQL.Result Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,17 +10,13 @@  Definitions parsing responses from a GraphQL API. -}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}--module Data.GraphQL.Result-  ( GraphQLResult(..)-  , getErrors-  , getResult-  ) where+module Data.GraphQL.Result (+  GraphQLResult (..),+  getErrors,+  getResult,+) where -import Data.Aeson (FromJSON(..), withObject, (.!=), (.:?))+import Data.Aeson (FromJSON (..), withObject, (.!=), (.:?))  import Data.GraphQL.Error (GraphQLError) @@ -24,7 +24,8 @@ data GraphQLResult r = GraphQLResult   { resultErrors :: [GraphQLError]   , resultResult :: Maybe r-  } deriving (Show,Functor,Foldable,Traversable)+  }+  deriving (Show, Functor, Foldable, Traversable)  instance FromJSON r => FromJSON (GraphQLResult r) where   parseJSON = withObject "GraphQLResult" $ \o ->
src/Data/GraphQL/TestUtils.hs view
@@ -1,4 +1,10 @@-{-|+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}++{- | Module      :  Data.GraphQL.TestUtils Maintainer  :  Brandon Chinn <brandon@leapyear.io> Stability   :  experimental@@ -6,19 +12,13 @@  Defines test utilities for testing GraphQL queries. -}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-}--module Data.GraphQL.TestUtils-  ( ResultMock(..)-  , mocked-  , MockQueryT-  , runMockQueryT-  , AnyResultMock-  ) where+module Data.GraphQL.TestUtils (+  ResultMock (..),+  mocked,+  MockQueryT,+  runMockQueryT,+  AnyResultMock,+) where  import Control.Monad.IO.Class (MonadIO) import Control.Monad.State.Strict (MonadState, StateT, evalStateT, state)@@ -28,13 +28,14 @@ import qualified Data.Text as Text  import Data.GraphQL.Error (GraphQLError)-import Data.GraphQL.Monad (MonadGraphQLQuery(..))-import Data.GraphQL.Query (GraphQLQuery(..))+import Data.GraphQL.Monad (MonadGraphQLQuery (..))+import Data.GraphQL.Query (GraphQLQuery (..))  data ResultMock query = ResultMock-  { query  :: query+  { query :: query   , result :: Value-  } deriving (Show)+  }+  deriving (Show)  mocked :: (Show query, GraphQLQuery query) => ResultMock query -> AnyResultMock mocked = AnyResultMock@@ -53,7 +54,7 @@  {- MockQueryT -} -newtype MockQueryT m a = MockQueryT { unMockQueryT :: StateT [AnyResultMock] m a }+newtype MockQueryT m a = MockQueryT {unMockQueryT :: StateT [AnyResultMock] m a}   deriving (Functor, Applicative, Monad, MonadIO, MonadState [AnyResultMock], MonadTrans)  instance Monad m => MonadGraphQLQuery (MockQueryT m) where@@ -61,7 +62,7 @@     where       takeWhere :: (a -> Bool) -> [a] -> Maybe (a, [a])       takeWhere f xs = case break f xs of-        (before, match:after) -> Just (match, before ++ after)+        (before, match : after) -> Just (match, before ++ after)         (_, []) -> Nothing        -- Find the first matching mock and remove it from the state@@ -72,10 +73,12 @@           Nothing -> error $ "No more mocked responses for query: " ++ Text.unpack (getQueryName testQuery)        toGraphQLResult :: FromJSON a => Value -> a-      toGraphQLResult mockData = either error id . Aeson.parseEither Aeson.parseJSON $ object-        [ "errors" .= ([] :: [GraphQLError])-        , "data"   .= Just mockData-        ]+      toGraphQLResult mockData =+        either error id . Aeson.parseEither Aeson.parseJSON $+          object+            [ "errors" .= ([] :: [GraphQLError])+            , "data" .= Just mockData+            ]  runMockQueryT :: Monad m => MockQueryT m a -> [AnyResultMock] -> m a runMockQueryT mockQueryT mocks = (`evalStateT` mocks) . unMockQueryT $ mockQueryT
test/Data/GraphQL/Test/Monad/Class.hs view
@@ -15,12 +15,12 @@ import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (testCase, (@?=)) -import Data.GraphQL.Error (GraphQLError(..), GraphQLException(..))-import Data.GraphQL.Monad (MonadGraphQLQuery(..), runQuery)-import Data.GraphQL.Result (GraphQLResult(..))-import Data.GraphQL.Test.TestQuery (TestQuery(..))+import Data.GraphQL.Error (GraphQLError (..), GraphQLException (..))+import Data.GraphQL.Monad (MonadGraphQLQuery (..), runQuery)+import Data.GraphQL.Result (GraphQLResult (..))+import Data.GraphQL.Test.TestQuery (TestQuery (..)) -newtype MockQueryM a = MockQueryM { unMock :: ReaderT (Either GraphQLError Value) IO a }+newtype MockQueryM a = MockQueryM {unMock :: ReaderT (Either GraphQLError Value) IO a}   deriving (Functor, Applicative, Monad, MonadIO, MonadReader (Either GraphQLError Value))  runMockQueryM :: Either GraphQLError Value -> MockQueryM a -> IO a@@ -34,15 +34,15 @@         Right v -> GraphQLResult [] $ Aeson.parseMaybe Aeson.parseJSON v  testRunQuery :: TestTree-testRunQuery = testGroup "runQuery <-> runQuerySafe"-  [ testCase "runQuery throws if runQuerySafe returns an error" $ do-      let err = GraphQLError "Something went wrong" Nothing Nothing-      result <- try $ runMockQueryM (Left err) (runQuery TestQuery)-      show <$> result @?= Left (GraphQLException [err])--  , testCase "runQuery returns the result of runQuerySafe" $ do-      let v = object [ "getUser" .= object [ "id" .= (1 :: Int) ] ]-      result <- runMockQueryM (Right v) (runQuery TestQuery)-      [get| result.getUser.id |] @?= 1-  ]-+testRunQuery =+  testGroup+    "runQuery <-> runQuerySafe"+    [ testCase "runQuery throws if runQuerySafe returns an error" $ do+        let err = GraphQLError "Something went wrong" Nothing Nothing+        result <- try $ runMockQueryM (Left err) (runQuery TestQuery)+        show <$> result @?= Left (GraphQLException [err])+    , testCase "runQuery returns the result of runQuerySafe" $ do+        let v = object ["getUser" .= object ["id" .= (1 :: Int)]]+        result <- runMockQueryM (Right v) (runQuery TestQuery)+        [get| result.getUser.id |] @?= 1+    ]
test/Data/GraphQL/Test/TestQuery.hs view
@@ -11,7 +11,8 @@ data TestQuery = TestQuery   deriving (Show) -type TestSchema = [schema|+type TestSchema =+  [schema|   {     getUser: {       id: Int@@ -22,7 +23,8 @@ instance GraphQLQuery TestQuery where   type ResultSchema TestQuery = TestSchema   getQueryName _ = "test"-  getQueryText _ = [query|+  getQueryText _ =+    [query|     query test($name: String!) {       getUser(name: $name) {         id
test/Data/GraphQL/Test/TestUtils.hs view
@@ -13,32 +13,41 @@ import Test.Tasty.HUnit (assertFailure, testCase, (@?), (@?=))  import Data.GraphQL.Monad (runQuery)-import Data.GraphQL.Test.TestQuery (TestQuery(..))-import Data.GraphQL.TestUtils (ResultMock(..), mocked, runMockQueryT)+import Data.GraphQL.Test.TestQuery (TestQuery (..))+import Data.GraphQL.TestUtils (ResultMock (..), mocked, runMockQueryT)  testTestUtils :: TestTree-testTestUtils = testGroup "TestUtils"-  [ testCase "MockQueryT returns a mock" $ do-      obj <- runMockQueryT runTestQuery-        [ mocked ResultMock-            { query = TestQuery-            , result = object [ "getUser" .= object [ "id" .= (20 :: Int) ] ]-            }-        ]-      [get| obj.getUser.id |] @?= 20-  , testCase "MockQueryT errors with no matching mock" $ do-      obj <- try @SomeException $ runMockQueryT runTestQuery []-      case obj of-        Right _ -> assertFailure "MockQueryT did not error"-        Left e -> (head . lines . show) e @?= "No more mocked responses for query: test"-  , testCase "MockQueryT removes mock after use" $ do-      obj <- try @SomeException $ runMockQueryT (runTestQuery >> runTestQuery)-        [ mocked ResultMock-            { query = TestQuery-            , result = object [ "getUser" .= object [ "id" .= (20 :: Int) ] ]-            }-        ]-      isLeft obj @? "MockQueryT did not error"-  ]+testTestUtils =+  testGroup+    "TestUtils"+    [ testCase "MockQueryT returns a mock" $ do+        obj <-+          runMockQueryT+            runTestQuery+            [ mocked+                ResultMock+                  { query = TestQuery+                  , result = object ["getUser" .= object ["id" .= (20 :: Int)]]+                  }+            ]+        [get| obj.getUser.id |] @?= 20+    , testCase "MockQueryT errors with no matching mock" $ do+        obj <- try @SomeException $ runMockQueryT runTestQuery []+        case obj of+          Right _ -> assertFailure "MockQueryT did not error"+          Left e -> (head . lines . show) e @?= "No more mocked responses for query: test"+    , testCase "MockQueryT removes mock after use" $ do+        obj <-+          try @SomeException $+            runMockQueryT+              (runTestQuery >> runTestQuery)+              [ mocked+                  ResultMock+                    { query = TestQuery+                    , result = object ["getUser" .= object ["id" .= (20 :: Int)]]+                    }+              ]+        isLeft obj @? "MockQueryT did not error"+    ]   where     runTestQuery = runQuery TestQuery
test/Main.hs view
@@ -4,7 +4,10 @@ import Data.GraphQL.Test.TestUtils  main :: IO ()-main = defaultMain $ testGroup "graphql-client"-  [ testRunQuery-  , testTestUtils-  ]+main =+  defaultMain $+    testGroup+      "graphql-client"+      [ testRunQuery+      , testTestUtils+      ]