From 28b3d6cafdc24df7d085ca84eaa8de8b4094be56 Mon Sep 17 00:00:00 2001 From: Xavier Francisco Date: Thu, 23 May 2019 16:44:34 +0100 Subject: [PATCH] Update stylish haskell config; apply all; add CI config (#1299) * Update config default; Copy non-defaults * Update .stylish-haskell config version to match pgrst * Apply stylish haskell to all files * CircleCI config * Remove redundant import. What is used from Network.HTTP.Types.Headers is also exported by Network.HTTP.Types. * Grouped imports * Show un-styled files on CircleCI failure * Fix styling imports * Apply adhoc standard correctly --- .circleci/config.yml | 5 +- .stylish-haskell.yaml | 45 ++++++++++-- main/Main.hs | 65 +++++++++--------- src/PostgREST/ApiRequest.hs | 98 +++++++++++++++------------ src/PostgREST/App.hs | 90 +++++++++++------------- src/PostgREST/Auth.hs | 24 ++++--- src/PostgREST/Config.hs | 69 ++++++++++--------- src/PostgREST/DbRequestBuilder.hs | 67 +++++++++--------- src/PostgREST/DbStructure.hs | 38 +++++------ src/PostgREST/Error.hs | 17 +++-- src/PostgREST/Middleware.hs | 32 +++++---- src/PostgREST/OpenAPI.hs | 50 +++++++------- src/PostgREST/Parsers.hs | 35 +++++----- src/PostgREST/QueryBuilder.hs | 56 +++++++-------- src/PostgREST/RangeQuery.hs | 20 +++--- src/PostgREST/Types.hs | 45 ++++++------ test/Feature/AndOrParamsSpec.hs | 11 +-- test/Feature/AsymmetricJwtSpec.hs | 10 +-- test/Feature/AudienceJwtSecretSpec.hs | 14 ++-- test/Feature/AuthSpec.hs | 13 ++-- test/Feature/BinaryJwtSecretSpec.hs | 10 +-- test/Feature/ConcurrentSpec.hs | 19 +++--- test/Feature/CorsSpec.hs | 10 +-- test/Feature/DeleteSpec.hs | 6 +- test/Feature/ExtraSearchPathSpec.hs | 7 +- test/Feature/InsertSpec.hs | 31 ++++----- test/Feature/JsonOperatorSpec.hs | 11 ++- test/Feature/NoJwtSpec.hs | 9 +-- test/Feature/NonexistentSchemaSpec.hs | 4 +- test/Feature/PgVersion95Spec.hs | 6 +- test/Feature/PgVersion96Spec.hs | 6 +- test/Feature/ProxySpec.hs | 9 ++- test/Feature/QueryLimitedSpec.hs | 11 +-- test/Feature/QuerySpec.hs | 11 +-- test/Feature/RangeSpec.hs | 14 ++-- test/Feature/RpcSpec.hs | 16 ++--- test/Feature/SingularSpec.hs | 12 ++-- test/Feature/StructureSpec.hs | 30 ++++---- test/Feature/UnicodeSpec.hs | 8 +-- test/Feature/UpsertSpec.hs | 12 ++-- test/Main.hs | 49 +++++++------- test/SpecHelper.hs | 45 ++++++------ test/TestTypes.hs | 12 ++-- 43 files changed, 603 insertions(+), 549 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 99935ed23..db565e8e4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,7 +77,7 @@ jobs: sudo apt-get install -y postgresql-client stack setup rm -rf $(stack path --dist-dir) $(stack path --local-install-root) - stack install hlint packdeps cabal-install + stack install hlint packdeps cabal-install stylish-haskell - run: name: build src and tests command: | @@ -91,6 +91,9 @@ jobs: - run: name: run linter command: git ls-files | grep '\.l\?hs$' | xargs stack exec -- hlint -X QuasiQuotes -X NoPatternSynonyms "$@" + - run: + name: run styler + command: git ls-files | grep '\.l\?hs$' | xargs stack exec -- stylish-haskell -i && git diff-index --exit-code HEAD -- - save_cache: paths: - "~/.stack" diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml index 016013c37..3bdbfc88c 100644 --- a/.stylish-haskell.yaml +++ b/.stylish-haskell.yaml @@ -39,9 +39,9 @@ steps: # - none: Do not perform any alignment. # # Default: global. - align: global + align: group - # Folowing options affect only import list alignment. + # The following options affect only import list alignment. # # List align has following options: # @@ -64,6 +64,25 @@ steps: # Default: after_alias list_align: after_alias + # Right-pad the module names to align imports in a group: + # + # - true: a little more readable + # + # > import qualified Data.List as List (concat, foldl, foldr, + # > init, last, length) + # > import qualified Data.List.Extra as List (concat, foldl, foldr, + # > init, last, length) + # + # - false: diff-safe + # + # > import qualified Data.List as List (concat, foldl, foldr, init, + # > last, length) + # > import qualified Data.List.Extra as List (concat, foldl, foldr, + # > init, last, length) + # + # Default: true + pad_module_names: true + # Long list align style takes effect when import is too long. This is # determined by 'columns' setting. # @@ -75,7 +94,7 @@ steps: # short enough to fit to single line. Otherwise it'll be multiline. # # - multiline: One line per import list entry. - # Type with contructor list acts like single import. + # Type with constructor list acts like single import. # # > import qualified Data.Map as M # > ( empty @@ -109,7 +128,7 @@ steps: # Useful for 'file' and 'group' align settings. list_padding: 4 - # Separate lists option affects formating of import list for type + # Separate lists option affects formatting of import list for type # or class. The only difference is single space between type and list # of constructors, selectors and class functions. # @@ -126,6 +145,22 @@ steps: # Default: true separate_lists: true + # Space surround option affects formatting of import lists on a single + # line. The only difference is single space after the initial + # parenthesis and a single space before the terminal parenthesis. + # + # - true: There is single space associated with the enclosing + # parenthesis. + # + # > import Data.Foo ( foo ) + # + # - false: There is no space associated with the enclosing parenthesis + # + # > import Data.Foo (foo) + # + # Default: false + space_surround: false + # Language pragmas - language_pragmas: # We can generate different styles of language pragma lists. @@ -142,7 +177,7 @@ steps: # Align affects alignment of closing pragma brackets. # - # - true: Brackets are aligned in same collumn. + # - true: Brackets are aligned in same column. # # - false: Brackets are not aligned together. There is only one space # between actual import and closing bracket. diff --git a/main/Main.hs b/main/Main.hs index 6aa34c08b..861683224 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -2,41 +2,42 @@ module Main where - -import PostgREST.App (postgrest) -import PostgREST.Config (AppConfig (..), configPoolTimeout', - prettyVersion, readOptions) -import PostgREST.DbStructure (getDbStructure, getPgVersion) -import PostgREST.Error (errorPayload, checkIsFatal, PgError(PgError)) -import PostgREST.OpenAPI (isMalformedProxyUri) -import PostgREST.Types (DbStructure, Schema, PgVersion(..), minimumPgVersion, ConnectionStatus(..)) -import Protolude hiding (hPutStrLn, replace) - - -import Control.AutoUpdate (defaultUpdateSettings, - mkAutoUpdate, updateAction) -import Control.Retry (RetryStatus, capDelay, - exponentialBackoff, - retrying, rsPreviousDelay) import qualified Data.ByteString as BS import qualified Data.ByteString.Base64 as B64 -import Data.IORef (IORef, atomicWriteIORef, - newIORef, readIORef) -import Data.String (IsString (..)) -import Data.Text (pack, replace, stripPrefix, strip) -import Data.Text.Encoding (decodeUtf8, encodeUtf8) -import Data.Text.IO (hPutStrLn, readFile) -import Data.Time.Clock (getCurrentTime) import qualified Hasql.Pool as P import qualified Hasql.Transaction.Sessions as HT -import Network.Wai.Handler.Warp (defaultSettings, - runSettings, setHost, - setPort, setServerName) -import System.IO (BufferMode (..), - hSetBuffering) + +import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate, + updateAction) +import Control.Retry (RetryStatus, capDelay, + exponentialBackoff, retrying, + rsPreviousDelay) +import Data.IORef (IORef, atomicWriteIORef, newIORef, + readIORef) +import Data.String (IsString (..)) +import Data.Text (pack, replace, strip, stripPrefix) +import Data.Text.Encoding (decodeUtf8, encodeUtf8) +import Data.Text.IO (hPutStrLn, readFile) +import Data.Time.Clock (getCurrentTime) +import Network.Wai.Handler.Warp (defaultSettings, runSettings, + setHost, setPort, setServerName) +import System.IO (BufferMode (..), hSetBuffering) + +import PostgREST.App (postgrest) +import PostgREST.Config (AppConfig (..), configPoolTimeout', + prettyVersion, readOptions) +import PostgREST.DbStructure (getDbStructure, getPgVersion) +import PostgREST.Error (PgError (PgError), checkIsFatal, + errorPayload) +import PostgREST.OpenAPI (isMalformedProxyUri) +import PostgREST.Types (ConnectionStatus (..), DbStructure, + PgVersion (..), Schema, + minimumPgVersion) +import Protolude hiding (hPutStrLn, replace) + #ifndef mingw32_HOST_OS -import System.Posix.Signals +import System.Posix.Signals #endif {-| @@ -75,7 +76,7 @@ connectionWorker mainTid pool schema refDbStructure refIsWorkerOn = do putStrLn ("Attempting to connect to the database..." :: Text) connected <- connectionStatus pool case connected of - FatalConnectionError reason -> hPutStrLn stderr reason + FatalConnectionError reason -> hPutStrLn stderr reason >> killThread mainTid -- Fatal error when connecting NotConnected -> return () -- Unreachable Connected actualPgVersion -> do -- Procede with initialization @@ -87,7 +88,7 @@ connectionWorker mainTid pool schema refDbStructure refIsWorkerOn = do putStrLn ("Failed to query the database. Retrying." :: Text) hPutStrLn stderr . toS . errorPayload $ PgError False e work - + Right _ -> do atomicWriteIORef refIsWorkerOn False putStrLn ("Connection successful" :: Text) @@ -298,6 +299,6 @@ loadDbUriFile conf = extractDbUri mDbUri extractDbUri dbUri = fmap setDbUri $ case stripPrefix "@" dbUri of - Nothing -> return dbUri + Nothing -> return dbUri Just filename -> strip <$> readFile (toS filename) setDbUri dbUri = conf {configDatabase = dbUri} diff --git a/src/PostgREST/ApiRequest.hs b/src/PostgREST/ApiRequest.hs index e646eb69d..a7c52a702 100644 --- a/src/PostgREST/ApiRequest.hs +++ b/src/PostgREST/ApiRequest.hs @@ -4,7 +4,7 @@ Description : PostgREST functions to translate HTTP request to a domain type cal -} {-# LANGUAGE LambdaCase #-} -module PostgREST.ApiRequest ( +module PostgREST.ApiRequest ( ApiRequest(..) , ContentType(..) , Action(..) @@ -14,33 +14,41 @@ module PostgREST.ApiRequest ( , userApiRequest ) where -import Protolude -import qualified Data.Aeson as JSON -import Data.Aeson.Types (emptyObject, emptyArray) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Internal as BS (c2w) -import qualified Data.ByteString.Lazy as BL -import qualified Data.Csv as CSV -import qualified Data.List as L -import Data.List (lookup, last, partition) -import qualified Data.HashMap.Strict as M -import qualified Data.Set as S -import Data.Maybe (fromJust) -import Control.Arrow ((***)) -import qualified Data.Text as T -import qualified Data.Vector as V -import Network.HTTP.Base (urlEncodeVars) -import Network.HTTP.Types.Header (hAuthorization, hCookie) -import Network.HTTP.Types.URI (parseSimpleQuery, parseQueryReplacePlus) -import Network.Wai (Request (..)) -import Network.Wai.Parse (parseHttpAccept) -import PostgREST.RangeQuery (NonnegRange, rangeRequested, restrictRange, rangeGeq, allRange, rangeLimit, rangeOffset) -import Data.Ranged.Boundaries -import PostgREST.Types -import PostgREST.Error (ApiRequestError(..)) -import Data.Ranged.Ranges (Range(..), rangeIntersection, emptyRange) -import qualified Data.CaseInsensitive as CI -import Web.Cookie (parseCookiesText) +import qualified Data.Aeson as JSON +import qualified Data.ByteString as BS +import qualified Data.ByteString.Internal as BS (c2w) +import qualified Data.ByteString.Lazy as BL +import qualified Data.CaseInsensitive as CI +import qualified Data.Csv as CSV +import qualified Data.HashMap.Strict as M +import qualified Data.List as L +import qualified Data.Set as S +import qualified Data.Text as T +import qualified Data.Vector as V + +import Control.Arrow ((***)) +import Data.Aeson.Types (emptyArray, emptyObject) +import Data.List (last, lookup, partition) +import Data.Maybe (fromJust) +import Data.Ranged.Ranges (Range (..), emptyRange, + rangeIntersection) +import Network.HTTP.Base (urlEncodeVars) +import Network.HTTP.Types.Header (hAuthorization, hCookie) +import Network.HTTP.Types.URI (parseQueryReplacePlus, + parseSimpleQuery) +import Network.Wai (Request (..)) +import Network.Wai.Parse (parseHttpAccept) +import Web.Cookie (parseCookiesText) + + +import Data.Ranged.Boundaries + +import PostgREST.Error (ApiRequestError (..)) +import PostgREST.RangeQuery (NonnegRange, allRange, rangeGeq, + rangeLimit, rangeOffset, rangeRequested, + restrictRange) +import PostgREST.Types +import Protolude type RequestBody = BL.ByteString @@ -68,41 +76,41 @@ data PreferRepresentation = Full | HeadersOnly | None deriving Eq -} data ApiRequest = ApiRequest { -- | Similar but not identical to HTTP verb, e.g. Create/Invoke both POST - iAction :: Action + iAction :: Action -- | Requested range of rows within response - , iRange :: M.HashMap ByteString NonnegRange + , iRange :: M.HashMap ByteString NonnegRange -- | The target, be it calling a proc or accessing a table - , iTarget :: Target + , iTarget :: Target -- | Content types the client will accept, [CTAny] if no Accept header - , iAccepts :: [ContentType] + , iAccepts :: [ContentType] -- | Data sent by client and used for mutation actions - , iPayload :: Maybe PayloadJSON + , iPayload :: Maybe PayloadJSON -- | If client wants created items echoed back - , iPreferRepresentation :: PreferRepresentation + , iPreferRepresentation :: PreferRepresentation -- | Pass all parameters as a single json object to a stored procedure , iPreferSingleObjectParameter :: Bool -- | Whether the client wants a result count (slower) - , iPreferCount :: Bool + , iPreferCount :: Bool -- | Whether the client wants to UPSERT or ignore records on PK conflict - , iPreferResolution :: Maybe PreferResolution + , iPreferResolution :: Maybe PreferResolution -- | Filters on the result ("id", "eq.10") - , iFilters :: [(Text, Text)] + , iFilters :: [(Text, Text)] -- | &and and &or parameters used for complex boolean logic - , iLogic :: [(Text, Text)] + , iLogic :: [(Text, Text)] -- | &select parameter used to shape the response - , iSelect :: Text + , iSelect :: Text -- | &columns parameter used to shape the payload - , iColumns :: Maybe Text + , iColumns :: Maybe Text -- | &order parameters for each level - , iOrder :: [(Text, Text)] + , iOrder :: [(Text, Text)] -- | Alphabetized (canonical) request query string for response URLs - , iCanonicalQS :: ByteString + , iCanonicalQS :: ByteString -- | JSON Web Token - , iJWT :: Text + , iJWT :: Text -- | HTTP request headers - , iHeaders :: [(Text, Text)] + , iHeaders :: [(Text, Text)] -- | Request Cookies - , iCookies :: [(Text, Text)] + , iCookies :: [(Text, Text)] } -- | Examines HTTP request and translates it into user intent. diff --git a/src/PostgREST/App.hs b/src/PostgREST/App.hs index c7c37246d..5cb31bcfa 100644 --- a/src/PostgREST/App.hs +++ b/src/PostgREST/App.hs @@ -1,64 +1,56 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} module PostgREST.App ( postgrest ) where -import Control.Applicative -import Data.Aeson as JSON -import qualified Data.ByteString.Char8 as BS -import Data.Maybe -import Data.IORef (IORef, readIORef) -import Data.Time.Clock (UTCTime) -import qualified Data.Set as S - +import qualified Data.ByteString.Char8 as BS +import qualified Data.HashMap.Strict as M +import qualified Data.Set as S import qualified Hasql.Pool as P +import qualified Hasql.Transaction as H import qualified Hasql.Transaction as HT import qualified Hasql.Transaction.Sessions as HT -import Network.HTTP.Types.Header -import Network.HTTP.Types.Status -import Network.HTTP.Types.URI (renderSimpleQuery) -import Network.Wai -import Network.Wai.Middleware.RequestLogger (logStdout) +import Data.Aeson as JSON +import Data.Function (id) +import Data.IORef (IORef, readIORef) +import Data.Time.Clock (UTCTime) +import Network.HTTP.Types.URI (renderSimpleQuery) +import Network.Wai.Middleware.RequestLogger (logStdout) -import qualified Hasql.Transaction as H +import Control.Applicative +import Data.Maybe +import Network.HTTP.Types.Header +import Network.HTTP.Types.Status +import Network.Wai -import qualified Data.HashMap.Strict as M - -import PostgREST.ApiRequest ( ApiRequest(..), ContentType(..) - , Action(..), Target(..) - , PreferRepresentation (..) - , mutuallyAgreeable - , userApiRequest - ) -import PostgREST.Auth (jwtClaims, containsRole, parseSecret) -import PostgREST.Config (AppConfig (..)) -import PostgREST.DbStructure -import PostgREST.DbRequestBuilder( readRequest - , mutateRequest - , fieldNames - ) -import PostgREST.Error ( SimpleError(..), PgError(..) - , errorResponseFor - ) -import PostgREST.RangeQuery (allRange, rangeOffset) -import PostgREST.Middleware -import PostgREST.QueryBuilder ( callProc - , requestToQuery - , requestToCountQuery - , createReadStatement - , createWriteStatement - , ResultsWithCount - ) -import PostgREST.Parsers (pRequestColumns) -import PostgREST.Types -import PostgREST.OpenAPI - -import Data.Function (id) -import Protolude hiding (intercalate, Proxy) +import PostgREST.ApiRequest (Action (..), ApiRequest (..), + ContentType (..), + PreferRepresentation (..), + Target (..), mutuallyAgreeable, + userApiRequest) +import PostgREST.Auth (containsRole, jwtClaims, + parseSecret) +import PostgREST.Config (AppConfig (..)) +import PostgREST.DbRequestBuilder (fieldNames, mutateRequest, + readRequest) +import PostgREST.DbStructure +import PostgREST.Error (PgError (..), SimpleError (..), + errorResponseFor) +import PostgREST.Middleware +import PostgREST.OpenAPI +import PostgREST.Parsers (pRequestColumns) +import PostgREST.QueryBuilder (ResultsWithCount, callProc, + createReadStatement, + createWriteStatement, + requestToCountQuery, + requestToQuery) +import PostgREST.RangeQuery (allRange, rangeOffset) +import PostgREST.Types +import Protolude hiding (Proxy, intercalate) postgrest :: AppConfig -> IORef (Maybe DbStructure) -> P.Pool -> IO UTCTime -> IO () -> Application postgrest conf refDbStructure pool getTime worker = @@ -211,7 +203,7 @@ app dbStructure proc cols conf apiRequest = Right (sq, mq) -> do let isSingle = case pjType of PJArray len -> len == 1 - PJObject -> True + PJObject -> True colNames = colName <$> tableCols dbStructure tSchema tName if topLevelRange /= allRange then return . errorResponseFor $ PutRangeNotAllowedError diff --git a/src/PostgREST/Auth.hs b/src/PostgREST/Auth.hs index d11dbb936..4524cec90 100644 --- a/src/PostgREST/Auth.hs +++ b/src/PostgREST/Auth.hs @@ -1,5 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE LambdaCase #-} {-| Module : PostgREST.Auth Description : PostgREST authorization functions. @@ -19,17 +19,19 @@ module PostgREST.Auth ( , parseSecret ) where -import Control.Lens.Operators -import Control.Lens (set) -import qualified Data.Aeson as JSON -import qualified Data.HashMap.Strict as M -import Data.Time.Clock (UTCTime) -import Data.Vector as V -import PostgREST.Types -import Protolude +import qualified Crypto.JOSE.Types as JOSE.Types +import qualified Data.Aeson as JSON +import qualified Data.HashMap.Strict as M +import Data.Vector as V -import qualified Crypto.JOSE.Types as JOSE.Types -import Crypto.JWT +import Control.Lens (set) +import Data.Time.Clock (UTCTime) + +import Control.Lens.Operators +import Crypto.JWT + +import PostgREST.Types +import Protolude {-| Possible situations encountered with client JWTs diff --git a/src/PostgREST/Config.hs b/src/PostgREST/Config.hs index f51d9324c..59494c6e8 100644 --- a/src/PostgREST/Config.hs +++ b/src/PostgREST/Config.hs @@ -12,7 +12,8 @@ turned in configurable behaviour if needed. Other hardcoded options such as the minimum version number also belong here. -} -{-# LANGUAGE LambdaCase, TemplateHaskell #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} module PostgREST.Config ( prettyVersion @@ -24,43 +25,45 @@ module PostgREST.Config ( prettyVersion ) where -import Control.Applicative -import Control.Monad (fail) -import Control.Lens (preview) -import Crypto.JWT (StringOrURI, - stringOrUri) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BS import qualified Data.CaseInsensitive as CI import qualified Data.Configurator as C import qualified Data.Configurator.Parser as C import Data.Configurator.Types as C -import Data.List (lookup) -import Data.Monoid -import Data.Scientific (floatingOrInteger) -import Data.String (String) -import Data.Text (dropWhileEnd, dropEnd, - intercalate, lines, - strip, take, splitOn) -import Data.Text.Encoding (encodeUtf8) -import Data.Text.IO (hPutStrLn) -import Data.Version (versionBranch) -import Development.GitRev (gitHash) -import Network.Wai -import Network.Wai.Middleware.Cors (CorsResourcePolicy (..)) -import Options.Applicative hiding (str) -import Paths_postgrest (version) -import PostgREST.Parsers (pRoleClaimKey) -import PostgREST.Types (JSPath, JSPathExp(..)) -import PostgREST.Error (ApiRequestError(..)) -import Protolude hiding (hPutStrLn, take, - intercalate, (<>)) -import System.IO (hPrint) -import System.IO.Error (IOError) -import Text.Heredoc -import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>)) import qualified Text.PrettyPrint.ANSI.Leijen as L +import Control.Lens (preview) +import Control.Monad (fail) +import Crypto.JWT (StringOrURI, stringOrUri) +import Data.List (lookup) +import Data.Scientific (floatingOrInteger) +import Data.String (String) +import Data.Text (dropEnd, dropWhileEnd, + intercalate, lines, splitOn, + strip, take) +import Data.Text.Encoding (encodeUtf8) +import Data.Text.IO (hPutStrLn) +import Data.Version (versionBranch) +import Development.GitRev (gitHash) +import Network.Wai.Middleware.Cors (CorsResourcePolicy (..)) +import Paths_postgrest (version) +import System.IO (hPrint) +import System.IO.Error (IOError) + +import Control.Applicative +import Data.Monoid +import Network.Wai +import Options.Applicative hiding (str) +import Text.Heredoc +import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>)) + +import PostgREST.Error (ApiRequestError (..)) +import PostgREST.Parsers (pRoleClaimKey) +import PostgREST.Types (JSPath, JSPathExp (..)) +import Protolude hiding (hPutStrLn, intercalate, take, (<>)) + + -- | Config file settings for the server data AppConfig = AppConfig { configDatabase :: Text @@ -171,7 +174,7 @@ readOptions = do coerceText :: Value -> Text coerceText (String s) = s - coerceText v = show v + coerceText v = show v coerceInt :: (Read i, Integral i) => Value -> Maybe i coerceInt (Number x) = rightToMaybe $ floatingOrInteger x @@ -185,11 +188,11 @@ readOptions = do parseRoleClaimKey :: Value -> Either ApiRequestError JSPath parseRoleClaimKey (String s) = pRoleClaimKey s - parseRoleClaimKey v = pRoleClaimKey $ show v + parseRoleClaimKey v = pRoleClaimKey $ show v splitExtraSearchPath :: Value -> [Text] splitExtraSearchPath (String s) = strip <$> splitOn "," s - splitExtraSearchPath _ = [] + splitExtraSearchPath _ = [] opts = info (helper <*> pathParser) $ fullDesc diff --git a/src/PostgREST/DbRequestBuilder.hs b/src/PostgREST/DbRequestBuilder.hs index f19676e6b..a2a8911f5 100644 --- a/src/PostgREST/DbRequestBuilder.hs +++ b/src/PostgREST/DbRequestBuilder.hs @@ -6,11 +6,11 @@ This module is in charge of building an intermediate representation(ReadRequest, A query tree is built in case of resource embedding. By inferring the relationship between tables, join conditions are added for every embedded resource. -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE DuplicateRecordFields#-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE NamedFieldPuns #-} module PostgREST.DbRequestBuilder ( readRequest @@ -18,36 +18,33 @@ module PostgREST.DbRequestBuilder ( , fieldNames ) where -import Control.Applicative -import Control.Arrow ((***)) -import Control.Lens.Getter (view) -import Control.Lens.Tuple (_1) -import qualified Data.ByteString.Char8 as BS -import Data.List (delete) -import Data.Maybe (fromJust) -import qualified Data.Set as S -import Data.Text (isInfixOf) -import Data.Tree -import Data.Either.Combinators (mapLeft) +import qualified Data.ByteString.Char8 as BS +import qualified Data.HashMap.Strict as M +import qualified Data.Set as S -import Network.Wai +import Control.Arrow ((***)) +import Control.Lens.Getter (view) +import Control.Lens.Tuple (_1) +import Data.Either.Combinators (mapLeft) +import Data.Foldable (foldr1) +import Data.List (delete) +import Data.Maybe (fromJust) +import Data.Text (isInfixOf) +import Text.Regex.TDFA ((=~)) +import Unsafe (unsafeHead) -import Data.Foldable (foldr1) -import qualified Data.HashMap.Strict as M +import Control.Applicative +import Data.Tree +import Network.Wai -import PostgREST.ApiRequest ( ApiRequest(..) - , PreferRepresentation(..) - , Action(..), Target(..) - , PreferRepresentation (..) - ) -import PostgREST.Error (ApiRequestError(..), errorResponseFor) -import PostgREST.Parsers -import PostgREST.RangeQuery (NonnegRange, restrictRange, allRange) -import PostgREST.Types - -import Protolude hiding (from) -import Text.Regex.TDFA ((=~)) -import Unsafe (unsafeHead) +import PostgREST.ApiRequest (Action (..), ApiRequest (..), + PreferRepresentation (..), + PreferRepresentation (..), Target (..)) +import PostgREST.Error (ApiRequestError (..), errorResponseFor) +import PostgREST.Parsers +import PostgREST.RangeQuery (NonnegRange, allRange, restrictRange) +import PostgREST.Types +import Protolude hiding (from) readRequest :: Maybe Integer -> [Relation] -> Maybe ProcDescription -> ApiRequest -> Either Response ReadRequest readRequest maxRows allRels proc apiRequest = @@ -65,9 +62,9 @@ readRequest maxRows allRels proc apiRequest = (TargetProc (QualifiedIdentifier s pName) ) -> Just (s, tName) where tName = case pdReturnType <$> proc of - Just (SetOf (Composite qi)) -> qiName qi + Just (SetOf (Composite qi)) -> qiName qi Just (Single (Composite qi)) -> qiName qi - _ -> pName + _ -> pName _ -> Nothing @@ -93,7 +90,7 @@ readRequest maxRows allRels proc apiRequest = ActionUpdate -> fakeSourceRelations ++ allRels ActionDelete -> fakeSourceRelations ++ allRels ActionInvoke _ -> fakeSourceRelations ++ allRels - _ -> allRels + _ -> allRels where fakeSourceRelations = mapMaybe (toSourceRelation rootTableName) allRels -- in a relation where one of the tables matches "TableName" diff --git a/src/PostgREST/DbStructure.hs b/src/PostgREST/DbStructure.hs index bbe8043f8..5636b7a8a 100644 --- a/src/PostgREST/DbStructure.hs +++ b/src/PostgREST/DbStructure.hs @@ -10,10 +10,10 @@ These queries are executed once at startup or when PostgREST is reloaded. -} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeSynonymInstances #-} -{-# LANGUAGE NamedFieldPuns #-} module PostgREST.DbStructure ( getDbStructure , accessibleTables @@ -22,26 +22,26 @@ module PostgREST.DbStructure ( , getPgVersion ) where -import qualified Hasql.Decoders as HD -import qualified Hasql.Encoders as HE -import qualified Hasql.Statement as H +import qualified Data.HashMap.Strict as M +import qualified Data.List as L +import qualified Data.Text as T +import qualified Hasql.Decoders as HD +import qualified Hasql.Encoders as HE +import qualified Hasql.Session as H +import qualified Hasql.Statement as H +import qualified Hasql.Transaction as HT -import Control.Applicative -import qualified Data.HashMap.Strict as M -import qualified Data.List as L -import Data.Set as S (fromList) -import Data.Text (split, strip, - breakOn, dropAround, - splitOn) -import qualified Data.Text as T -import qualified Hasql.Session as H -import qualified Hasql.Transaction as HT -import PostgREST.Types -import Text.InterpolatedString.Perl6 (q, qc) +import Data.Set as S (fromList) +import Data.Text (breakOn, dropAround, split, + splitOn, strip) +import GHC.Exts (groupWith) +import Text.InterpolatedString.Perl6 (q, qc) +import Unsafe (unsafeHead) -import GHC.Exts (groupWith) -import Protolude -import Unsafe (unsafeHead) +import Control.Applicative + +import PostgREST.Types +import Protolude getDbStructure :: Schema -> PgVersion -> HT.Transaction DbStructure getDbStructure schema pgVer = do diff --git a/src/PostgREST/Error.hs b/src/PostgREST/Error.hs index 448aefb08..42a82aac8 100644 --- a/src/PostgREST/Error.hs +++ b/src/PostgREST/Error.hs @@ -15,17 +15,20 @@ module PostgREST.Error ( , checkIsFatal ) where -import Protolude -import Data.Aeson ((.=)) import qualified Data.Aeson as JSON -import Data.Text (unwords) import qualified Hasql.Pool as P import qualified Hasql.Session as H -import Network.HTTP.Types.Header import qualified Network.HTTP.Types.Status as HT -import Network.Wai (Response, responseLBS) -import PostgREST.Types -import Text.Read (readMaybe) + +import Data.Aeson ((.=)) +import Data.Text (unwords) +import Network.Wai (Response, responseLBS) +import Text.Read (readMaybe) + +import Network.HTTP.Types.Header + +import PostgREST.Types +import Protolude class (JSON.ToJSON a) => PgrstError a where diff --git a/src/PostgREST/Middleware.hs b/src/PostgREST/Middleware.hs index 41550098e..765cd2f95 100644 --- a/src/PostgREST/Middleware.hs +++ b/src/PostgREST/Middleware.hs @@ -3,28 +3,30 @@ Module : PostgREST.Middleware Description : Sets the PostgreSQL GUCs, role, search_path and pre-request function. Validates JWT. -} {-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE FlexibleContexts #-} module PostgREST.Middleware where -import Crypto.JWT -import qualified Data.Aeson as JSON -import qualified Data.HashMap.Strict as M -import qualified Hasql.Transaction as H +import qualified Data.Aeson as JSON +import qualified Data.HashMap.Strict as M +import qualified Hasql.Transaction as H -import Network.Wai (Application, Response) -import Network.Wai.Middleware.Cors (cors) -import Network.Wai.Middleware.Gzip (def, gzip) -import Network.Wai.Middleware.Static (only, staticPolicy) +import Network.Wai (Application, Response) +import Network.Wai.Middleware.Cors (cors) +import Network.Wai.Middleware.Gzip (def, gzip) +import Network.Wai.Middleware.Static (only, staticPolicy) -import PostgREST.ApiRequest (ApiRequest(..)) -import PostgREST.Auth (JWTAttempt(..)) -import PostgREST.Config (AppConfig (..), corsPolicy) -import PostgREST.Error (errorResponseFor, SimpleError(JwtTokenMissing, JwtTokenInvalid)) -import PostgREST.QueryBuilder (unquoted, pgFmtSetLocal, pgFmtSetLocalSearchPath) +import Crypto.JWT -import Protolude +import PostgREST.ApiRequest (ApiRequest (..)) +import PostgREST.Auth (JWTAttempt (..)) +import PostgREST.Config (AppConfig (..), corsPolicy) +import PostgREST.Error (SimpleError (JwtTokenInvalid, JwtTokenMissing), + errorResponseFor) +import PostgREST.QueryBuilder (pgFmtSetLocal, pgFmtSetLocalSearchPath, + unquoted) +import Protolude runWithClaims :: AppConfig -> JWTAttempt -> (ApiRequest -> H.Transaction Response) -> diff --git a/src/PostgREST/OpenAPI.hs b/src/PostgREST/OpenAPI.hs index 559435620..c1df03804 100644 --- a/src/PostgREST/OpenAPI.hs +++ b/src/PostgREST/OpenAPI.hs @@ -10,25 +10,29 @@ module PostgREST.OpenAPI ( , pickProxy ) where -import Control.Arrow ((&&&)) -import Control.Lens -import Data.Aeson (decode, encode) -import Data.HashMap.Strict.InsOrd (InsOrdHashMap, fromList) -import Data.Maybe (fromJust) -import qualified Data.Set as Set -import Data.String (IsString (..)) -import Data.Text (unpack, pack, init, tail, toLower, intercalate, append, dropWhile, breakOn) -import Network.URI (parseURI, isAbsoluteURI, - URI (..), URIAuth (..)) +import qualified Data.Set as Set -import Protolude hiding ((&), Proxy, get, intercalate, dropWhile) +import Control.Arrow ((&&&)) +import Data.Aeson (decode, encode) +import Data.HashMap.Strict.InsOrd (InsOrdHashMap, fromList) +import Data.Maybe (fromJust) +import Data.String (IsString (..)) +import Data.Text (append, breakOn, dropWhile, init, + intercalate, pack, tail, toLower, + unpack) +import Network.URI (URI (..), URIAuth (..), + isAbsoluteURI, parseURI) -import Data.Swagger +import Control.Lens +import Data.Swagger -import PostgREST.ApiRequest (ContentType(..)) -import PostgREST.Config (prettyVersion, docsVersion) -import PostgREST.Types (Table(..), Column(..), PgArg(..), ForeignKey(..), - PrimaryKey(..), Proxy(..), ProcDescription(..), toMime) +import PostgREST.ApiRequest (ContentType (..)) +import PostgREST.Config (docsVersion, prettyVersion) +import PostgREST.Types (Column (..), ForeignKey (..), PgArg (..), + PrimaryKey (..), ProcDescription (..), + Proxy (..), Table (..), toMime) +import Protolude hiding (Proxy, dropWhile, get, + intercalate, (&)) makeMimeList :: [ContentType] -> MimeList makeMimeList cs = MimeList $ map (fromString . toS . toMime) cs @@ -329,7 +333,7 @@ pickProxy proxy uri = toURI $ fromJust proxy scheme = init $ toLower $ pack $ uriScheme uri path URI {uriPath = ""} = "/" - path URI {uriPath = p} = p + path URI {uriPath = p} = p path' = pack $ path uri authority = fromJust $ uriAuthority uri host' = pack $ uriRegName authority @@ -337,9 +341,9 @@ pickProxy proxy readPort = fromMaybe 80 . readMaybe port'' :: Integer port'' = case (port', scheme) of - ("", "http") -> 80 + ("", "http") -> 80 ("", "https") -> 443 - _ -> readPort $ unpack $ tail $ pack port' + _ -> readPort $ unpack $ tail $ pack port' isUriValid:: URI -> Bool isUriValid = fAnd [isSchemeValid, isQueryValid, isAuthorityValid] @@ -355,7 +359,7 @@ isSchemeValid URI {uriScheme = s} isQueryValid :: URI -> Bool isQueryValid URI {uriQuery = ""} = True -isQueryValid _ = False +isQueryValid _ = False isAuthorityValid :: URI -> Bool isAuthorityValid URI {uriAuthority = a} @@ -364,16 +368,16 @@ isAuthorityValid URI {uriAuthority = a} isUserInfoValid :: URIAuth -> Bool isUserInfoValid URIAuth {uriUserInfo = ""} = True -isUserInfoValid _ = False +isUserInfoValid _ = False isHostValid :: URIAuth -> Bool isHostValid URIAuth {uriRegName = ""} = False -isHostValid _ = True +isHostValid _ = True isPortValid :: URIAuth -> Bool isPortValid URIAuth {uriPort = ""} = True isPortValid URIAuth {uriPort = (':':p)} = case readMaybe p of - Just i -> i > (0 :: Integer) && i < 65536 + Just i -> i > (0 :: Integer) && i < 65536 Nothing -> False isPortValid _ = False diff --git a/src/PostgREST/Parsers.hs b/src/PostgREST/Parsers.hs index 50717656f..81857b467 100644 --- a/src/PostgREST/Parsers.hs +++ b/src/PostgREST/Parsers.hs @@ -6,22 +6,25 @@ This module is in charge of parsing all the querystring values in an url, e.g. t -} module PostgREST.Parsers where -import Protolude hiding (try, intercalate, replace, option) -import Control.Monad ((>>)) -import Data.Foldable (foldl1) -import Data.Functor (($>)) -import qualified Data.HashMap.Strict as M -import Data.Text (intercalate, replace, strip) -import Data.List (init, last) -import qualified Data.Set as S -import Data.Tree -import Data.Either.Combinators (mapLeft) -import PostgREST.RangeQuery (NonnegRange) -import PostgREST.Error (ApiRequestError(ParseRequestError)) -import PostgREST.Types -import Text.ParserCombinators.Parsec hiding (many, (<|>)) -import Text.Parsec.Error -import Text.Read (read) +import qualified Data.HashMap.Strict as M +import qualified Data.Set as S + +import Control.Monad ((>>)) +import Data.Either.Combinators (mapLeft) +import Data.Foldable (foldl1) +import Data.Functor (($>)) +import Data.List (init, last) +import Data.Text (intercalate, replace, strip) +import Text.Read (read) + +import Data.Tree +import Text.Parsec.Error +import Text.ParserCombinators.Parsec hiding (many, (<|>)) + +import PostgREST.Error (ApiRequestError (ParseRequestError)) +import PostgREST.RangeQuery (NonnegRange) +import PostgREST.Types +import Protolude hiding (intercalate, option, replace, try) pRequestSelect :: Text -> Either ApiRequestError [Tree SelectItem] pRequestSelect selStr = diff --git a/src/PostgREST/QueryBuilder.hs b/src/PostgREST/QueryBuilder.hs index 9cee521e2..658a37064 100644 --- a/src/PostgREST/QueryBuilder.hs +++ b/src/PostgREST/QueryBuilder.hs @@ -1,6 +1,6 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-| @@ -27,29 +27,29 @@ module PostgREST.QueryBuilder ( , pgFmtSetLocalSearchPath ) where -import qualified Hasql.Statement as H -import qualified Hasql.Encoders as HE -import qualified Hasql.Decoders as HD +import qualified Data.Aeson as JSON +import qualified Data.ByteString.Char8 as BS +import qualified Data.HashMap.Strict as HM +import qualified Data.Set as S +import qualified Data.Text as T (map, null, takeWhile) +import qualified Data.Text.Encoding as T +import qualified Hasql.Decoders as HD +import qualified Hasql.Encoders as HE +import qualified Hasql.Statement as H -import qualified Data.Aeson as JSON +import Data.Scientific (FPFormat (..), formatScientific, + isInteger) +import Data.Text (intercalate, isInfixOf, replace, + toLower, unwords) +import Data.Tree (Tree (..)) +import Text.InterpolatedString.Perl6 (qc) -import PostgREST.RangeQuery (rangeLimit, rangeOffset, allRange) -import qualified Data.HashMap.Strict as HM -import Data.Maybe -import qualified Data.Set as S -import Data.Text (intercalate, unwords, replace, isInfixOf, toLower) -import qualified Data.Text as T (map, takeWhile, null) -import qualified Data.Text.Encoding as T -import Data.Tree (Tree(..)) -import PostgREST.Types -import Text.InterpolatedString.Perl6 (qc) -import qualified Data.ByteString.Char8 as BS -import Data.Scientific ( FPFormat (..) - , formatScientific - , isInteger - ) -import Protolude hiding ( intercalate, cast, replace) -import PostgREST.ApiRequest (PreferRepresentation (..)) +import Data.Maybe + +import PostgREST.ApiRequest (PreferRepresentation (..)) +import PostgREST.RangeQuery (allRange, rangeLimit, rangeOffset) +import PostgREST.Types +import Protolude hiding (cast, intercalate, replace) {-| The generic query result format used by API responses. The location header is represented as a list of strings containing variable bindings like @@ -409,7 +409,7 @@ emptyOnFalse val cond = if cond then "" else val pgFmtColumn :: QualifiedIdentifier -> Text -> SqlFragment pgFmtColumn table "*" = fromQi table <> ".*" -pgFmtColumn table c = fromQi table <> "." <> pgFmtIdent c +pgFmtColumn table c = fromQi table <> "." <> pgFmtIdent c pgFmtField :: QualifiedIdentifier -> Field -> SqlFragment pgFmtField table (c, jp) = pgFmtColumn table c <> pgFmtJsonPath jp @@ -427,10 +427,10 @@ pgFmtOrderTerm qi ot = unwords [ pgFmtFilter :: QualifiedIdentifier -> Filter -> SqlFragment pgFmtFilter table (Filter fld (OpExpr hasNot oper)) = notOp <> " " <> case oper of Op op val -> pgFmtFieldOp op <> " " <> case op of - "like" -> unknownLiteral (T.map star val) - "ilike" -> unknownLiteral (T.map star val) - "is" -> whiteList val - _ -> unknownLiteral val + "like" -> unknownLiteral (T.map star val) + "ilike" -> unknownLiteral (T.map star val) + "is" -> whiteList val + _ -> unknownLiteral val In vals -> pgFmtField table fld <> " " <> let emptyValForIn = "= any('{}') " in -- Workaround because for postgresql "col IN ()" is invalid syntax, we instead do "col = any('{}')" diff --git a/src/PostgREST/RangeQuery.hs b/src/PostgREST/RangeQuery.hs index 4819d6d12..285487233 100644 --- a/src/PostgREST/RangeQuery.hs +++ b/src/PostgREST/RangeQuery.hs @@ -13,19 +13,17 @@ module PostgREST.RangeQuery ( , NonnegRange ) where +import qualified Data.ByteString.Char8 as BS -import Control.Applicative -import Network.HTTP.Types.Header +import Data.List (lookup) +import Text.Regex.TDFA ((=~)) -import qualified Data.ByteString.Char8 as BS -import Data.Ranged.Boundaries -import Data.Ranged.Ranges +import Control.Applicative +import Data.Ranged.Boundaries +import Data.Ranged.Ranges +import Network.HTTP.Types.Header -import Text.Regex.TDFA ((=~)) - -import Data.List (lookup) - -import Protolude +import Protolude type NonnegRange = Range Integer @@ -60,7 +58,7 @@ rangeOffset :: NonnegRange -> Integer rangeOffset range = case rangeLower range of BoundaryBelow lower -> lower - _ -> panic "range without lower bound" -- should never happen + _ -> panic "range without lower bound" -- should never happen rangeGeq :: Integer -> NonnegRange rangeGeq n = diff --git a/src/PostgREST/Types.hs b/src/PostgREST/Types.hs index c2037d8c9..5eed8c662 100644 --- a/src/PostgREST/Types.hs +++ b/src/PostgREST/Types.hs @@ -2,20 +2,23 @@ Module : PostgREST.Types Description : PostgREST common types and functions used by the rest of the modules -} -{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE DuplicateRecordFields #-} module PostgREST.Types where -import Protolude -import qualified GHC.Show import qualified Data.Aeson as JSON import qualified Data.ByteString.Lazy as BL import qualified Data.CaseInsensitive as CI import qualified Data.HashMap.Strict as M -import qualified Data.Set as S -import Data.Tree -import PostgREST.RangeQuery (NonnegRange) -import Network.HTTP.Types.Header (hContentType, Header) +import qualified Data.Set as S +import qualified GHC.Show + +import Network.HTTP.Types.Header (Header, hContentType) + +import Data.Tree + +import PostgREST.RangeQuery (NonnegRange) +import Protolude -- | Enumeration of currently supported response content types data ContentType = CTApplicationJSON | CTTextCSV | CTOpenAPI @@ -224,10 +227,10 @@ data PayloadJSON = data PJType = PJArray { pjaLength :: Int } | PJObject deriving (Show, Eq) data Proxy = Proxy { - proxyScheme :: Text -, proxyHost :: Text -, proxyPort :: Integer -, proxyPath :: Text + proxyScheme :: Text +, proxyHost :: Text +, proxyPort :: Integer +, proxyPath :: Text } deriving (Show, Eq) type Operator = Text @@ -272,8 +275,8 @@ type ListVal = [Text] data LogicOperator = And | Or deriving Eq instance Show LogicOperator where - show And = "AND" - show Or = "OR" + show And = "AND" + show Or = "OR" {-| Boolean logic expression tree e.g. "and(name.eq.N,or(id.eq.1,id.eq.2))" is: @@ -353,15 +356,15 @@ data MutateQuery = , returning :: [FieldName] }| Update { - in_ :: TableName - , updCols :: S.Set FieldName - , where_ :: [LogicTree] - , returning :: [FieldName] + in_ :: TableName + , updCols :: S.Set FieldName + , where_ :: [LogicTree] + , returning :: [FieldName] }| Delete { - in_ :: TableName - , where_ :: [LogicTree] - , returning :: [FieldName] + in_ :: TableName + , where_ :: [LogicTree] + , returning :: [FieldName] } deriving (Show, Eq) data DbRequest = DbRead ReadRequest | DbMutate MutateRequest @@ -409,7 +412,7 @@ data JSPathExp = JSPKey Text | JSPIdx Int deriving (Eq, Show) -- | Current database connection status data ConnectionStatus -data ConnectionStatus +data ConnectionStatus = NotConnected | Connected PgVersion | FatalConnectionError Text diff --git a/test/Feature/AndOrParamsSpec.hs b/test/Feature/AndOrParamsSpec.hs index c3092942e..08ae9ff0a 100644 --- a/test/Feature/AndOrParamsSpec.hs +++ b/test/Feature/AndOrParamsSpec.hs @@ -1,13 +1,14 @@ module Feature.AndOrParamsSpec where -import Test.Hspec -import Test.Hspec.Wai -import Test.Hspec.Wai.JSON -import Network.HTTP.Types import Network.Wai (Application) +import Network.HTTP.Types +import Test.Hspec +import Test.Hspec.Wai +import Test.Hspec.Wai.JSON + +import Protolude hiding (get) import SpecHelper -import Protolude hiding (get) spec :: SpecWith Application diff --git a/test/Feature/AsymmetricJwtSpec.hs b/test/Feature/AsymmetricJwtSpec.hs index 95c5a3f04..780220a25 100644 --- a/test/Feature/AsymmetricJwtSpec.hs +++ b/test/Feature/AsymmetricJwtSpec.hs @@ -1,14 +1,14 @@ module Feature.AsymmetricJwtSpec where -- {{{ Imports -import Test.Hspec -import Test.Hspec.Wai -import Network.HTTP.Types - -import SpecHelper import Network.Wai (Application) +import Network.HTTP.Types +import Test.Hspec +import Test.Hspec.Wai + import Protolude +import SpecHelper -- }}} spec :: SpecWith Application diff --git a/test/Feature/AudienceJwtSecretSpec.hs b/test/Feature/AudienceJwtSecretSpec.hs index 35be4dd88..6cd5912b1 100644 --- a/test/Feature/AudienceJwtSecretSpec.hs +++ b/test/Feature/AudienceJwtSecretSpec.hs @@ -1,14 +1,14 @@ module Feature.AudienceJwtSecretSpec where -- {{{ Imports -import Test.Hspec -import Test.Hspec.Wai -import Network.HTTP.Types - -import SpecHelper import Network.Wai (Application) -import Protolude hiding (get) +import Network.HTTP.Types +import Test.Hspec +import Test.Hspec.Wai + +import Protolude hiding (get) +import SpecHelper -- }}} spec :: SpecWith Application @@ -44,4 +44,4 @@ spec = describe "test handling of aud claims in JWT" $ do `shouldRespondWith` 200 it "requests without JWT token should work" $ - get "/has_count_column" `shouldRespondWith` 200 \ No newline at end of file + get "/has_count_column" `shouldRespondWith` 200 diff --git a/test/Feature/AuthSpec.hs b/test/Feature/AuthSpec.hs index 50d3f9ca3..64bc79bdc 100644 --- a/test/Feature/AuthSpec.hs +++ b/test/Feature/AuthSpec.hs @@ -1,17 +1,16 @@ module Feature.AuthSpec where -import Text.Heredoc +import Network.Wai (Application) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types - -import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get) +import Text.Heredoc import PostgREST.Types (PgVersion, pgVersion112) +import Protolude hiding (get) +import SpecHelper spec :: PgVersion -> SpecWith Application spec actualPgVersion = describe "authorization" $ do diff --git a/test/Feature/BinaryJwtSecretSpec.hs b/test/Feature/BinaryJwtSecretSpec.hs index f42196144..6229aad93 100644 --- a/test/Feature/BinaryJwtSecretSpec.hs +++ b/test/Feature/BinaryJwtSecretSpec.hs @@ -1,14 +1,14 @@ module Feature.BinaryJwtSecretSpec where -- {{{ Imports -import Test.Hspec -import Test.Hspec.Wai -import Network.HTTP.Types - -import SpecHelper import Network.Wai (Application) +import Network.HTTP.Types +import Test.Hspec +import Test.Hspec.Wai + import Protolude +import SpecHelper -- }}} spec :: SpecWith Application diff --git a/test/Feature/ConcurrentSpec.hs b/test/Feature/ConcurrentSpec.hs index d7d412188..ffbb79ed9 100644 --- a/test/Feature/ConcurrentSpec.hs +++ b/test/Feature/ConcurrentSpec.hs @@ -1,20 +1,21 @@ -{-# LANGUAGE MultiParamTypeClasses, TypeFamilies, UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Feature.ConcurrentSpec where -import Control.Monad (void) -import Control.Monad.Base - -import Control.Monad.Trans.Control import Control.Concurrent.Async (mapConcurrently) +import Control.Monad (void) +import Network.Wai (Application) +import Control.Monad.Base +import Control.Monad.Trans.Control + +import Network.Wai.Test (Session) import Test.Hspec -import Test.Hspec.Wai.Internal import Test.Hspec.Wai +import Test.Hspec.Wai.Internal import Test.Hspec.Wai.JSON -import Network.Wai.Test (Session) - -import Network.Wai (Application) import Protolude hiding (get) diff --git a/test/Feature/CorsSpec.hs b/test/Feature/CorsSpec.hs index 142488f58..6115c33e1 100644 --- a/test/Feature/CorsSpec.hs +++ b/test/Feature/CorsSpec.hs @@ -1,17 +1,17 @@ module Feature.CorsSpec where -- {{{ Imports -import Test.Hspec -import Test.Hspec.Wai -import Network.Wai.Test (SResponse(simpleHeaders, simpleBody)) import qualified Data.ByteString.Lazy as BL -import SpecHelper +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleBody, simpleHeaders)) import Network.HTTP.Types -import Network.Wai (Application) +import Test.Hspec +import Test.Hspec.Wai import Protolude +import SpecHelper -- }}} spec :: SpecWith Application diff --git a/test/Feature/DeleteSpec.hs b/test/Feature/DeleteSpec.hs index ffc9a4fca..eb799db0a 100644 --- a/test/Feature/DeleteSpec.hs +++ b/test/Feature/DeleteSpec.hs @@ -1,12 +1,12 @@ module Feature.DeleteSpec where +import Network.Wai (Application) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Text.Heredoc -import Network.HTTP.Types -import Network.Wai (Application) - import Protolude hiding (get) spec :: SpecWith Application diff --git a/test/Feature/ExtraSearchPathSpec.hs b/test/Feature/ExtraSearchPathSpec.hs index d45acaee9..c1c2e1126 100644 --- a/test/Feature/ExtraSearchPathSpec.hs +++ b/test/Feature/ExtraSearchPathSpec.hs @@ -1,14 +1,13 @@ module Feature.ExtraSearchPathSpec where +import Network.HTTP.Types +import Network.Wai (Application) import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types - -import SpecHelper -import Network.Wai (Application) import Protolude +import SpecHelper spec :: SpecWith Application spec = describe "extra search path" $ do diff --git a/test/Feature/InsertSpec.hs b/test/Feature/InsertSpec.hs index e2f92e2aa..8f31cd602 100644 --- a/test/Feature/InsertSpec.hs +++ b/test/Feature/InsertSpec.hs @@ -1,27 +1,24 @@ module Feature.InsertSpec where -import Test.Hspec hiding (pendingWith) +import qualified Data.Aeson as JSON + +import Control.Monad (replicateM_, void) +import Data.List (lookup) +import Data.Maybe (fromJust) +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus)) +import Test.Hspec hiding (pendingWith) +import Test.Hspec.Wai.Matcher (bodyEquals) +import TestTypes (CompoundPK (..), IncPK (..)) + +import Network.HTTP.Types import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Test.Hspec.Wai.Matcher (bodyEquals) -import Network.Wai.Test (SResponse(simpleBody,simpleHeaders,simpleStatus)) - -import SpecHelper - -import qualified Data.Aeson as JSON -import Data.List (lookup) -import Data.Maybe (fromJust) import Text.Heredoc -import Network.HTTP.Types.Header -import Network.HTTP.Types -import Control.Monad (replicateM_, void) - -import TestTypes(IncPK(..), CompoundPK(..)) -import Network.Wai (Application) - -import Protolude hiding (get) import PostgREST.Types (PgVersion, pgVersion112) +import Protolude hiding (get) +import SpecHelper spec :: PgVersion -> SpecWith Application spec actualPgVersion = do diff --git a/test/Feature/JsonOperatorSpec.hs b/test/Feature/JsonOperatorSpec.hs index 58c35aafd..1f3b4166c 100644 --- a/test/Feature/JsonOperatorSpec.hs +++ b/test/Feature/JsonOperatorSpec.hs @@ -1,16 +1,15 @@ module Feature.JsonOperatorSpec where +import Network.Wai (Application) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types - -import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get) import PostgREST.Types (PgVersion, pgVersion112) +import Protolude hiding (get) +import SpecHelper spec :: PgVersion -> SpecWith Application spec actualPgVersion = describe "json and jsonb operators" $ do diff --git a/test/Feature/NoJwtSpec.hs b/test/Feature/NoJwtSpec.hs index 7874cb3d3..453bd8fd6 100644 --- a/test/Feature/NoJwtSpec.hs +++ b/test/Feature/NoJwtSpec.hs @@ -1,15 +1,16 @@ module Feature.NoJwtSpec where -- {{{ Imports + +import Network.Wai (Application) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types - -import SpecHelper -import Network.Wai (Application) import Protolude +import SpecHelper -- }}} spec :: SpecWith Application diff --git a/test/Feature/NonexistentSchemaSpec.hs b/test/Feature/NonexistentSchemaSpec.hs index e9444905e..ed1d5dce4 100644 --- a/test/Feature/NonexistentSchemaSpec.hs +++ b/test/Feature/NonexistentSchemaSpec.hs @@ -1,10 +1,12 @@ module Feature.NonexistentSchemaSpec where import Network.Wai (Application) -import Protolude hiding (get) + import Test.Hspec import Test.Hspec.Wai +import Protolude hiding (get) + spec :: SpecWith Application spec = describe "Non existent api schema" $ do diff --git a/test/Feature/PgVersion95Spec.hs b/test/Feature/PgVersion95Spec.hs index b36abbde6..dc5cedc66 100644 --- a/test/Feature/PgVersion95Spec.hs +++ b/test/Feature/PgVersion95Spec.hs @@ -1,13 +1,13 @@ module Feature.PgVersion95Spec where +import Network.Wai (Application) + import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON +import Protolude hiding (get) import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get) spec :: SpecWith Application spec = describe "features supported on PostgreSQL 9.5" $ diff --git a/test/Feature/PgVersion96Spec.hs b/test/Feature/PgVersion96Spec.hs index 392885a7a..de0ce17ba 100644 --- a/test/Feature/PgVersion96Spec.hs +++ b/test/Feature/PgVersion96Spec.hs @@ -1,13 +1,13 @@ module Feature.PgVersion96Spec where +import Network.Wai (Application) + import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON +import Protolude hiding (get) import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get) spec :: SpecWith Application spec = diff --git a/test/Feature/ProxySpec.hs b/test/Feature/ProxySpec.hs index 34a5c2fa8..dba05af85 100644 --- a/test/Feature/ProxySpec.hs +++ b/test/Feature/ProxySpec.hs @@ -1,13 +1,12 @@ module Feature.ProxySpec where -import Test.Hspec hiding (pendingWith) +import Network.Wai (Application) +import Test.Hspec hiding (pendingWith) + import Test.Hspec.Wai -import SpecHelper - -import Network.Wai (Application) - import Protolude +import SpecHelper spec :: SpecWith Application spec = diff --git a/test/Feature/QueryLimitedSpec.hs b/test/Feature/QueryLimitedSpec.hs index 39200c679..e79b0817f 100644 --- a/test/Feature/QueryLimitedSpec.hs +++ b/test/Feature/QueryLimitedSpec.hs @@ -1,14 +1,15 @@ module Feature.QueryLimitedSpec where +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleHeaders, simpleStatus)) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types -import Network.Wai.Test (SResponse(simpleHeaders, simpleStatus)) -import SpecHelper -import Network.Wai (Application) -import Protolude hiding (get) +import Protolude hiding (get) +import SpecHelper spec :: SpecWith Application spec = diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index af2b0560d..4e185c39b 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -1,16 +1,17 @@ module Feature.QuerySpec where +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleHeaders)) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types -import Network.Wai.Test (SResponse(simpleHeaders)) -import SpecHelper import Text.Heredoc -import Network.Wai (Application) -import Protolude hiding (get) +import Protolude hiding (get) +import SpecHelper spec :: SpecWith Application spec = do diff --git a/test/Feature/RangeSpec.hs b/test/Feature/RangeSpec.hs index d3fde5596..c1c8f033e 100644 --- a/test/Feature/RangeSpec.hs +++ b/test/Feature/RangeSpec.hs @@ -1,17 +1,17 @@ module Feature.RangeSpec where +import qualified Data.ByteString.Lazy as BL + +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleHeaders, simpleStatus)) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types -import Network.Wai.Test (SResponse(simpleHeaders,simpleStatus)) - -import qualified Data.ByteString.Lazy as BL +import Protolude hiding (get) import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get) defaultRange :: BL.ByteString defaultRange = [json| { "min": 0, "max": 15 } |] diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index 64cdecda6..c00c51b02 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -1,19 +1,19 @@ module Feature.RpcSpec where +import qualified Data.ByteString.Lazy as BL (empty) + +import Network.Wai (Application) +import Network.Wai.Test (SResponse (simpleBody, simpleStatus)) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types -import Network.Wai.Test (SResponse(simpleStatus, simpleBody)) -import qualified Data.ByteString.Lazy as BL (empty) - -import SpecHelper import Text.Heredoc -import Network.Wai (Application) - -import Protolude hiding (get) import PostgREST.Types (PgVersion, pgVersion95) +import Protolude hiding (get) +import SpecHelper spec :: PgVersion -> SpecWith Application spec actualPgVersion = diff --git a/test/Feature/SingularSpec.hs b/test/Feature/SingularSpec.hs index c389ba026..4e1633717 100644 --- a/test/Feature/SingularSpec.hs +++ b/test/Feature/SingularSpec.hs @@ -1,16 +1,16 @@ module Feature.SingularSpec where -import Text.Heredoc +import Network.Wai (Application) +import Network.Wai.Test (SResponse (..)) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types -import Network.Wai.Test (SResponse(..)) - -import Network.Wai (Application) +import Text.Heredoc +import Protolude hiding (get) import SpecHelper -import Protolude hiding (get) spec :: SpecWith Application diff --git a/test/Feature/StructureSpec.hs b/test/Feature/StructureSpec.hs index 2feb4459c..bbae04b70 100644 --- a/test/Feature/StructureSpec.hs +++ b/test/Feature/StructureSpec.hs @@ -1,22 +1,22 @@ module Feature.StructureSpec where -import Test.Hspec hiding (pendingWith) -import Test.Hspec.Wai -import Network.HTTP.Types - -import PostgREST.Config (docsVersion) -import Control.Lens ((^?)) +import Control.Lens ((^?)) import Data.Aeson.Types (Value (..)) +import Network.Wai (Application) +import Network.Wai.Test (SResponse (..)) + import Data.Aeson.Lens import Data.Aeson.QQ +import Network.HTTP.Types +import Test.Hspec hiding (pendingWith) +import Test.Hspec.Wai + + +import PostgREST.Config (docsVersion) +import Protolude hiding (get) import SpecHelper -import Network.Wai (Application) -import Network.Wai.Test (SResponse(..)) - -import Protolude hiding (get) - spec :: SpecWith Application spec = do @@ -251,7 +251,7 @@ spec = do "type": "string" } |] - + it "text to string" $ do r <- simpleBody <$> get "/" @@ -281,7 +281,7 @@ spec = do "type": "boolean" } |] - + it "smallint to integer" $ do r <- simpleBody <$> get "/" @@ -296,7 +296,7 @@ spec = do "type": "integer" } |] - + it "integer to integer" $ do r <- simpleBody <$> get "/" @@ -356,7 +356,7 @@ spec = do "type": "number" } |] - + it "double_precision to number" $ do r <- simpleBody <$> get "/" diff --git a/test/Feature/UnicodeSpec.hs b/test/Feature/UnicodeSpec.hs index 8b8955092..7111b20a9 100644 --- a/test/Feature/UnicodeSpec.hs +++ b/test/Feature/UnicodeSpec.hs @@ -1,15 +1,15 @@ module Feature.UnicodeSpec where +import Control.Monad (void) +import Network.Wai (Application) + import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.Wai (Application) -import Control.Monad (void) +import Protolude hiding (get) import SpecHelper -import Protolude hiding (get) - spec :: SpecWith Application spec = describe "Reading and writing to unicode schema and table names" $ diff --git a/test/Feature/UpsertSpec.hs b/test/Feature/UpsertSpec.hs index 0b51808bc..859e14b5a 100644 --- a/test/Feature/UpsertSpec.hs +++ b/test/Feature/UpsertSpec.hs @@ -1,16 +1,16 @@ module Feature.UpsertSpec where +import Network.Wai (Application) + +import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Network.HTTP.Types - -import SpecHelper -import Network.Wai (Application) - -import Protolude hiding (get, put) import Text.Heredoc +import Protolude hiding (get, put) +import SpecHelper + spec :: SpecWith Application spec = describe "UPSERT" $ do diff --git a/test/Main.hs b/test/Main.hs index 897bdf38d..b1c029c1b 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -1,23 +1,28 @@ module Main where -import Test.Hspec -import SpecHelper - -import qualified Hasql.Pool as P +import qualified Hasql.Pool as P import qualified Hasql.Transaction.Sessions as HT -import PostgREST.App (postgrest) -import PostgREST.DbStructure (getDbStructure, getPgVersion) -import PostgREST.Types (DbStructure(..), pgVersion95, pgVersion96) -import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate, updateAction) -import Data.Function (id) -import Data.IORef -import Data.Time.Clock (getCurrentTime) +import Control.AutoUpdate (defaultUpdateSettings, mkAutoUpdate, + updateAction) +import Data.Function (id) +import Data.Time.Clock (getCurrentTime) -import qualified Feature.AuthSpec +import Data.IORef +import Test.Hspec + +import PostgREST.App (postgrest) +import PostgREST.DbStructure (getDbStructure, getPgVersion) +import PostgREST.Types (DbStructure (..), pgVersion95, + pgVersion96) +import Protolude +import SpecHelper + +import qualified Feature.AndOrParamsSpec import qualified Feature.AsymmetricJwtSpec -import qualified Feature.BinaryJwtSecretSpec import qualified Feature.AudienceJwtSecretSpec +import qualified Feature.AuthSpec +import qualified Feature.BinaryJwtSecretSpec import qualified Feature.ConcurrentSpec import qualified Feature.CorsSpec import qualified Feature.DeleteSpec @@ -25,21 +30,19 @@ import qualified Feature.ExtraSearchPathSpec import qualified Feature.InsertSpec import qualified Feature.JsonOperatorSpec import qualified Feature.NoJwtSpec -import qualified Feature.QueryLimitedSpec -import qualified Feature.QuerySpec -import qualified Feature.RangeSpec -import qualified Feature.StructureSpec -import qualified Feature.SingularSpec -import qualified Feature.UnicodeSpec -import qualified Feature.ProxySpec -import qualified Feature.AndOrParamsSpec -import qualified Feature.RpcSpec import qualified Feature.NonexistentSchemaSpec import qualified Feature.PgVersion95Spec import qualified Feature.PgVersion96Spec +import qualified Feature.ProxySpec +import qualified Feature.QueryLimitedSpec +import qualified Feature.QuerySpec +import qualified Feature.RangeSpec +import qualified Feature.RpcSpec +import qualified Feature.SingularSpec +import qualified Feature.StructureSpec +import qualified Feature.UnicodeSpec import qualified Feature.UpsertSpec -import Protolude main :: IO () main = do diff --git a/test/SpecHelper.hs b/test/SpecHelper.hs index 5a2c3b9df..31b38dcfb 100644 --- a/test/SpecHelper.hs +++ b/test/SpecHelper.hs @@ -1,34 +1,31 @@ module SpecHelper where -import Control.Monad (void) +import qualified Data.ByteString.Base64 as B64 (decodeLenient, encode) +import qualified Data.ByteString.Char8 as BS +import qualified Data.ByteString.Lazy as BL +import qualified Data.Map.Strict as M +import qualified Data.Set as S +import qualified JSONSchema.Draft4 as D4 +import qualified System.IO.Error as E -import qualified System.IO.Error as E -import System.Environment (getEnv) +import Control.Monad (void) +import Data.Aeson (Value (..), decode) +import Data.CaseInsensitive (CI (..)) +import Data.List (lookup) +import Data.Maybe (fromJust) +import Network.Wai.Test (SResponse (simpleBody, simpleHeaders, simpleStatus)) +import System.Environment (getEnv) +import System.Process (readProcess) +import Text.Regex.TDFA ((=~)) -import qualified Data.ByteString.Base64 as B64 (encode, decodeLenient) -import Data.CaseInsensitive (CI(..)) -import qualified Data.Set as S -import qualified Data.Map.Strict as M -import Data.List (lookup) -import Text.Regex.TDFA ((=~)) -import qualified Data.ByteString.Char8 as BS -import qualified Data.ByteString.Lazy as BL -import System.Process (readProcess) -import Text.Heredoc - -import PostgREST.Config (AppConfig(..)) -import PostgREST.Types (JSPathExp(..)) - -import Test.Hspec -import Test.Hspec.Wai import Network.HTTP.Types -import Network.Wai.Test (SResponse(simpleStatus, simpleHeaders, simpleBody)) - -import Data.Maybe (fromJust) -import Data.Aeson (decode, Value(..)) -import qualified JSONSchema.Draft4 as D4 +import Test.Hspec +import Test.Hspec.Wai +import Text.Heredoc +import PostgREST.Config (AppConfig (..)) +import PostgREST.Types (JSPathExp (..)) import Protolude matchContentTypeJson :: MatchHeader diff --git a/test/TestTypes.hs b/test/TestTypes.hs index c6e99aa15..db1d4085b 100644 --- a/test/TestTypes.hs +++ b/test/TestTypes.hs @@ -3,16 +3,16 @@ module TestTypes ( , CompoundPK(..) ) where +import Data.Aeson ((.:)) import qualified Data.Aeson as JSON -import Data.Aeson ((.:)) import Protolude data IncPK = IncPK { - incId :: Int + incId :: Int , incNullableStr :: Maybe Text -, incStr :: Text -, incInsert :: Text +, incStr :: Text +, incInsert :: Text } deriving (Eq, Show) instance JSON.FromJSON IncPK where @@ -24,8 +24,8 @@ instance JSON.FromJSON IncPK where parseJSON _ = mzero data CompoundPK = CompoundPK { - compoundK1 :: Int -, compoundK2 :: Text + compoundK1 :: Int +, compoundK2 :: Text , compoundExtra :: Maybe Int } deriving (Eq, Show)