2022-03-17 23:53:56 +03:00
|
|
|
{-# LANGUAGE DuplicateRecordFields #-}
|
2022-03-16 03:39:21 +03:00
|
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
|
|
|
2022-02-18 16:35:32 +03:00
|
|
|
-- | Tests for remote relationships to remote schemas. Remote relationships are
|
|
|
|
-- relationships that are not local to a given source or remote schema, and are
|
|
|
|
-- handled by the engine itself.
|
|
|
|
--
|
|
|
|
-- All tests use the same GraphQL syntax, and the only difference is in the
|
|
|
|
-- setup: for each left-hand side source we support we do a custom setup and run
|
|
|
|
-- the tests.
|
2022-03-16 03:39:21 +03:00
|
|
|
module Test.RemoteRelationship.XToRemoteSchemaRelationshipSpec
|
|
|
|
( spec,
|
|
|
|
)
|
|
|
|
where
|
2022-02-18 16:35:32 +03:00
|
|
|
|
2022-03-17 23:53:56 +03:00
|
|
|
import Data.Char (isUpper, toLower)
|
2022-08-03 17:18:43 +03:00
|
|
|
import Data.List.NonEmpty qualified as NE
|
2022-03-17 23:53:56 +03:00
|
|
|
import Data.List.Split (dropBlanks, keepDelimsL, split, whenElt)
|
2022-02-18 16:35:32 +03:00
|
|
|
import Data.Morpheus.Document (gqlDocument)
|
2022-03-17 23:53:56 +03:00
|
|
|
import Data.Morpheus.Types
|
|
|
|
import Data.Morpheus.Types qualified as Morpheus
|
|
|
|
import Data.Typeable (Typeable)
|
2022-02-18 16:35:32 +03:00
|
|
|
import Harness.Backend.Postgres qualified as Postgres
|
2022-04-07 18:45:59 +03:00
|
|
|
import Harness.Backend.Sqlserver qualified as SQLServer
|
2022-02-18 16:35:32 +03:00
|
|
|
import Harness.GraphqlEngine qualified as GraphqlEngine
|
|
|
|
import Harness.Quoter.Graphql (graphql)
|
2022-08-05 12:34:25 +03:00
|
|
|
import Harness.Quoter.Yaml (yaml)
|
2022-02-18 16:35:32 +03:00
|
|
|
import Harness.RemoteServer qualified as RemoteServer
|
2022-02-21 20:05:09 +03:00
|
|
|
import Harness.Test.Context (Context (..))
|
|
|
|
import Harness.Test.Context qualified as Context
|
2022-06-17 11:44:04 +03:00
|
|
|
import Harness.Test.Schema (Table (..), table)
|
2022-03-10 14:18:13 +03:00
|
|
|
import Harness.Test.Schema qualified as Schema
|
2022-04-20 20:15:42 +03:00
|
|
|
import Harness.TestEnvironment (Server, TestEnvironment, stopServer)
|
2022-08-05 12:34:25 +03:00
|
|
|
import Harness.Yaml (shouldReturnYaml)
|
2022-08-03 17:18:43 +03:00
|
|
|
import Hasura.Prelude
|
2022-02-18 16:35:32 +03:00
|
|
|
import Test.Hspec (SpecWith, describe, it)
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Preamble
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
spec :: SpecWith TestEnvironment
|
|
|
|
spec = Context.runWithLocalTestEnvironment contexts tests
|
2022-02-18 16:35:32 +03:00
|
|
|
where
|
2022-08-03 17:18:43 +03:00
|
|
|
contexts = NE.fromList $ map mkContext [lhsPostgres, lhsSQLServer, lhsRemoteServer]
|
2022-02-18 16:35:32 +03:00
|
|
|
lhsPostgres =
|
|
|
|
Context
|
2022-03-15 19:08:47 +03:00
|
|
|
{ name = Context.Backend Context.Postgres,
|
2022-04-20 20:15:42 +03:00
|
|
|
mkLocalTestEnvironment = lhsPostgresMkLocalTestEnvironment,
|
2022-02-18 16:35:32 +03:00
|
|
|
setup = lhsPostgresSetup,
|
|
|
|
teardown = lhsPostgresTeardown,
|
|
|
|
customOptions = Nothing
|
|
|
|
}
|
2022-04-07 18:45:59 +03:00
|
|
|
lhsSQLServer =
|
|
|
|
Context
|
|
|
|
{ name = Context.Backend Context.SQLServer,
|
2022-04-20 20:15:42 +03:00
|
|
|
mkLocalTestEnvironment = lhsSQLServerMkLocalTestEnvironment,
|
2022-04-07 18:45:59 +03:00
|
|
|
setup = lhsSQLServerSetup,
|
|
|
|
teardown = lhsSQLServerTeardown,
|
|
|
|
customOptions = Nothing
|
|
|
|
}
|
2022-03-17 23:53:56 +03:00
|
|
|
lhsRemoteServer =
|
|
|
|
Context
|
|
|
|
{ name = Context.RemoteGraphQLServer,
|
2022-04-20 20:15:42 +03:00
|
|
|
mkLocalTestEnvironment = lhsRemoteServerMkLocalTestEnvironment,
|
2022-03-17 23:53:56 +03:00
|
|
|
setup = lhsRemoteServerSetup,
|
|
|
|
teardown = lhsRemoteServerTeardown,
|
|
|
|
customOptions = Nothing
|
|
|
|
}
|
2022-02-18 16:35:32 +03:00
|
|
|
|
|
|
|
-- | Uses a given RHS context to create a combined context.
|
2022-04-20 20:15:42 +03:00
|
|
|
mkContext :: Context (Maybe Server) -> Context LocalTestTestEnvironment
|
2022-02-18 16:35:32 +03:00
|
|
|
mkContext lhs =
|
|
|
|
Context
|
2022-02-21 20:05:09 +03:00
|
|
|
{ name = lhsName,
|
2022-04-20 20:15:42 +03:00
|
|
|
mkLocalTestEnvironment = \testEnvironment -> do
|
|
|
|
rhsServer <- rhsRemoteServerMkLocalTestEnvironment testEnvironment
|
|
|
|
lhsServer <- lhsMkLocalTestEnvironment testEnvironment
|
|
|
|
pure $ LocalTestTestEnvironment lhsServer rhsServer,
|
|
|
|
setup = \(testEnvironment, LocalTestTestEnvironment lhsServer rhsServer) -> do
|
|
|
|
rhsRemoteSchemaSetup (testEnvironment, rhsServer)
|
|
|
|
lhsSetup (testEnvironment, lhsServer),
|
|
|
|
teardown = \(testEnvironment, LocalTestTestEnvironment lhsServer rhsServer) -> do
|
|
|
|
lhsTeardown (testEnvironment, lhsServer)
|
|
|
|
rhsRemoteSchemaTeardown (testEnvironment, rhsServer)
|
|
|
|
GraphqlEngine.clearMetadata testEnvironment,
|
2022-02-18 16:35:32 +03:00
|
|
|
customOptions = lhsOptions
|
|
|
|
}
|
|
|
|
where
|
|
|
|
Context
|
|
|
|
{ name = lhsName,
|
2022-04-20 20:15:42 +03:00
|
|
|
mkLocalTestEnvironment = lhsMkLocalTestEnvironment,
|
2022-02-18 16:35:32 +03:00
|
|
|
setup = lhsSetup,
|
|
|
|
teardown = lhsTeardown,
|
|
|
|
customOptions = lhsOptions
|
|
|
|
} = lhs
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
-- | Local test testEnvironment.
|
|
|
|
data LocalTestTestEnvironment = LocalTestTestEnvironment
|
2022-02-18 16:35:32 +03:00
|
|
|
{ _lhsServer :: Maybe Server,
|
|
|
|
_rhsServer :: Server
|
|
|
|
}
|
|
|
|
|
2022-03-10 14:18:13 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Schema
|
|
|
|
|
|
|
|
-- | LHS
|
|
|
|
track :: Schema.Table
|
|
|
|
track =
|
2022-06-17 11:44:04 +03:00
|
|
|
(table "track")
|
|
|
|
{ tableColumns =
|
|
|
|
[ Schema.column "id" Schema.TInt,
|
|
|
|
Schema.column "title" Schema.TStr,
|
|
|
|
Schema.columnNull "album_id" Schema.TInt
|
|
|
|
],
|
|
|
|
tablePrimaryKey = ["id"],
|
|
|
|
tableData =
|
|
|
|
[ [Schema.VInt 1, Schema.VStr "track1_album1", Schema.VInt 1],
|
|
|
|
[Schema.VInt 2, Schema.VStr "track2_album1", Schema.VInt 1],
|
|
|
|
[Schema.VInt 3, Schema.VStr "track3_album1", Schema.VInt 1],
|
|
|
|
[Schema.VInt 4, Schema.VStr "track1_album2", Schema.VInt 2],
|
|
|
|
[Schema.VInt 5, Schema.VStr "track2_album2", Schema.VInt 2],
|
|
|
|
[Schema.VInt 6, Schema.VStr "track1_album3", Schema.VInt 3],
|
|
|
|
[Schema.VInt 7, Schema.VStr "track2_album3", Schema.VInt 3],
|
|
|
|
[Schema.VInt 8, Schema.VStr "track_no_album", Schema.VNull]
|
|
|
|
]
|
|
|
|
}
|
2022-03-10 14:18:13 +03:00
|
|
|
|
|
|
|
-- RHS schema is defined by the @gqlDocument@
|
|
|
|
|
2022-02-18 16:35:32 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- LHS Postgres
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsPostgresMkLocalTestEnvironment :: TestEnvironment -> IO (Maybe Server)
|
|
|
|
lhsPostgresMkLocalTestEnvironment _ = pure Nothing
|
2022-02-21 20:05:09 +03:00
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsPostgresSetup :: (TestEnvironment, Maybe Server) -> IO ()
|
|
|
|
lhsPostgresSetup (testEnvironment, _) = do
|
2022-03-10 14:18:13 +03:00
|
|
|
let sourceName = "source"
|
2022-02-18 16:35:32 +03:00
|
|
|
sourceConfig = Postgres.defaultSourceConfiguration
|
2022-03-15 19:08:47 +03:00
|
|
|
schemaName = Context.defaultSchema Context.Postgres
|
2022-03-10 14:18:13 +03:00
|
|
|
-- Add remote source
|
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-03-10 14:18:13 +03:00
|
|
|
[yaml|
|
|
|
|
type: pg_add_source
|
|
|
|
args:
|
|
|
|
name: *sourceName
|
|
|
|
configuration: *sourceConfig
|
|
|
|
|]
|
|
|
|
-- setup tables only
|
|
|
|
Postgres.createTable track
|
|
|
|
Postgres.insertTable track
|
2022-04-20 20:15:42 +03:00
|
|
|
Schema.trackTable Context.Postgres sourceName track testEnvironment
|
2022-02-18 16:35:32 +03:00
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-02-18 16:35:32 +03:00
|
|
|
[yaml|
|
|
|
|
type: bulk
|
|
|
|
args:
|
|
|
|
- type: pg_create_remote_relationship
|
|
|
|
args:
|
|
|
|
source: source
|
2022-03-10 14:18:13 +03:00
|
|
|
table:
|
|
|
|
schema: *schemaName
|
|
|
|
name: track
|
2022-02-18 16:35:32 +03:00
|
|
|
name: album
|
|
|
|
definition:
|
|
|
|
to_remote_schema:
|
|
|
|
remote_schema: target
|
|
|
|
lhs_fields: [album_id]
|
|
|
|
remote_field:
|
|
|
|
album:
|
|
|
|
arguments:
|
|
|
|
album_id: $album_id
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsPostgresTeardown :: (TestEnvironment, Maybe Server) -> IO ()
|
|
|
|
lhsPostgresTeardown (testEnvironment, _) = do
|
2022-03-10 14:18:13 +03:00
|
|
|
let sourceName = "source"
|
2022-04-20 20:15:42 +03:00
|
|
|
Schema.untrackTable Context.Postgres sourceName track testEnvironment
|
2022-03-10 14:18:13 +03:00
|
|
|
Postgres.dropTable track
|
2022-02-18 16:35:32 +03:00
|
|
|
|
2022-04-07 18:45:59 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- LHS SQLServer
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsSQLServerMkLocalTestEnvironment :: TestEnvironment -> IO (Maybe Server)
|
|
|
|
lhsSQLServerMkLocalTestEnvironment _ = pure Nothing
|
2022-04-07 18:45:59 +03:00
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsSQLServerSetup :: (TestEnvironment, Maybe Server) -> IO ()
|
|
|
|
lhsSQLServerSetup (testEnvironment, _) = do
|
2022-04-07 18:45:59 +03:00
|
|
|
let sourceName = "source"
|
|
|
|
sourceConfig = SQLServer.defaultSourceConfiguration
|
|
|
|
schemaName = Context.defaultSchema Context.SQLServer
|
|
|
|
-- Add remote source
|
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-04-07 18:45:59 +03:00
|
|
|
[yaml|
|
|
|
|
type: mssql_add_source
|
|
|
|
args:
|
|
|
|
name: *sourceName
|
|
|
|
configuration: *sourceConfig
|
|
|
|
|]
|
|
|
|
-- setup tables only
|
|
|
|
SQLServer.createTable track
|
|
|
|
SQLServer.insertTable track
|
2022-04-20 20:15:42 +03:00
|
|
|
Schema.trackTable Context.SQLServer sourceName track testEnvironment
|
2022-04-07 18:45:59 +03:00
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-04-07 18:45:59 +03:00
|
|
|
[yaml|
|
|
|
|
type: bulk
|
|
|
|
args:
|
|
|
|
- type: mssql_create_remote_relationship
|
|
|
|
args:
|
|
|
|
source: source
|
|
|
|
table:
|
|
|
|
schema: *schemaName
|
|
|
|
name: track
|
|
|
|
name: album
|
|
|
|
definition:
|
|
|
|
to_remote_schema:
|
|
|
|
remote_schema: target
|
|
|
|
lhs_fields: [album_id]
|
|
|
|
remote_field:
|
|
|
|
album:
|
|
|
|
arguments:
|
|
|
|
album_id: $album_id
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsSQLServerTeardown :: (TestEnvironment, Maybe Server) -> IO ()
|
|
|
|
lhsSQLServerTeardown (testEnvironment, _) = do
|
2022-04-07 18:45:59 +03:00
|
|
|
let sourceName = "source"
|
2022-04-20 20:15:42 +03:00
|
|
|
Schema.untrackTable Context.SQLServer sourceName track testEnvironment
|
2022-04-07 18:45:59 +03:00
|
|
|
SQLServer.dropTable track
|
|
|
|
|
2022-03-17 23:53:56 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- LHS Remote Server
|
|
|
|
|
|
|
|
-- | To circumvent Morpheus' default behaviour, which is to capitalize type
|
|
|
|
-- names and field names for Haskell records to be consistent with their
|
|
|
|
-- corresponding GraphQL equivalents, we define most of the schema manually with
|
|
|
|
-- the following options.
|
|
|
|
hasuraTypeOptions :: Morpheus.GQLTypeOptions
|
|
|
|
hasuraTypeOptions =
|
|
|
|
Morpheus.defaultTypeOptions
|
|
|
|
{ -- transformation to apply to constructors, for enums; we simply map to
|
|
|
|
-- lower case:
|
|
|
|
-- Asc -> asc
|
|
|
|
Morpheus.constructorTagModifier = map toLower,
|
|
|
|
-- transformation to apply to field names; we drop all characters up to and
|
|
|
|
-- including the first underscore:
|
|
|
|
-- hta_where -> where
|
|
|
|
Morpheus.fieldLabelModifier = tail . dropWhile (/= '_'),
|
|
|
|
-- transformation to apply to type names; we remove the leading "LHS" we
|
|
|
|
-- use to differentiate those types from the RHS ones, split the name on
|
|
|
|
-- uppercase letters, intercalate with underscore, and map everything to
|
|
|
|
-- lowercase: LHSHasuraTrack -> hasura_track
|
|
|
|
Morpheus.typeNameModifier = \_ ->
|
|
|
|
map toLower
|
|
|
|
. intercalate "_"
|
|
|
|
. split (dropBlanks $ keepDelimsL $ whenElt isUpper)
|
|
|
|
. drop 3
|
|
|
|
}
|
|
|
|
|
|
|
|
data LHSQuery m = LHSQuery
|
|
|
|
{ q_hasura_track :: LHSHasuraTrackArgs -> m [LHSHasuraTrack m]
|
|
|
|
}
|
|
|
|
deriving (Generic)
|
|
|
|
|
|
|
|
instance Typeable m => Morpheus.GQLType (LHSQuery m) where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
data LHSHasuraTrackArgs = LHSHasuraTrackArgs
|
|
|
|
{ ta_where :: Maybe LHSHasuraTrackBoolExp,
|
|
|
|
ta_order_by :: Maybe [LHSHasuraTrackOrderBy],
|
|
|
|
ta_limit :: Maybe Int
|
|
|
|
}
|
|
|
|
deriving (Generic)
|
|
|
|
|
|
|
|
instance Morpheus.GQLType LHSHasuraTrackArgs where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
data LHSHasuraTrack m = LHSHasuraTrack
|
|
|
|
{ t_id :: m (Maybe Int),
|
|
|
|
t_title :: m (Maybe Text),
|
|
|
|
t_album_id :: m (Maybe Int)
|
|
|
|
}
|
|
|
|
deriving (Generic)
|
|
|
|
|
|
|
|
instance Typeable m => Morpheus.GQLType (LHSHasuraTrack m) where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
data LHSHasuraTrackOrderBy = LHSHasuraTrackOrderBy
|
|
|
|
{ tob_id :: Maybe LHSOrderType,
|
|
|
|
tob_title :: Maybe LHSOrderType,
|
|
|
|
tob_album_id :: Maybe LHSOrderType
|
|
|
|
}
|
|
|
|
deriving (Generic)
|
|
|
|
|
|
|
|
instance Morpheus.GQLType LHSHasuraTrackOrderBy where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
data LHSHasuraTrackBoolExp = LHSHasuraTrackBoolExp
|
|
|
|
{ tbe__and :: Maybe [LHSHasuraTrackBoolExp],
|
|
|
|
tbe__or :: Maybe [LHSHasuraTrackBoolExp],
|
|
|
|
tbe__not :: Maybe LHSHasuraTrackBoolExp,
|
|
|
|
tbe_id :: Maybe IntCompExp,
|
|
|
|
tbe_title :: Maybe StringCompExp,
|
|
|
|
tbe_album_id :: Maybe IntCompExp
|
|
|
|
}
|
|
|
|
deriving (Generic)
|
|
|
|
|
|
|
|
instance Morpheus.GQLType LHSHasuraTrackBoolExp where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
data LHSOrderType = Asc | Desc
|
|
|
|
deriving (Show, Generic)
|
|
|
|
|
|
|
|
instance Morpheus.GQLType LHSOrderType where
|
|
|
|
typeOptions _ _ = hasuraTypeOptions
|
|
|
|
|
|
|
|
[gqlDocument|
|
|
|
|
|
|
|
|
input IntCompExp {
|
|
|
|
_eq: Int
|
|
|
|
}
|
|
|
|
|
|
|
|
input StringCompExp {
|
|
|
|
_eq: String
|
|
|
|
}
|
|
|
|
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsRemoteServerMkLocalTestEnvironment :: TestEnvironment -> IO (Maybe Server)
|
|
|
|
lhsRemoteServerMkLocalTestEnvironment _ = do
|
2022-03-17 23:53:56 +03:00
|
|
|
server <-
|
|
|
|
RemoteServer.run $
|
|
|
|
RemoteServer.generateQueryInterpreter (LHSQuery {q_hasura_track = hasura_track})
|
|
|
|
pure $ Just server
|
|
|
|
where
|
|
|
|
-- Implements the @hasura_track@ field of the @Query@ type.
|
|
|
|
hasura_track (LHSHasuraTrackArgs {..}) = do
|
|
|
|
let filterFunction = case ta_where of
|
|
|
|
Nothing -> const True
|
|
|
|
Just whereArg -> flip matchTrack whereArg
|
|
|
|
orderByFunction = case ta_order_by of
|
|
|
|
Nothing -> \_ _ -> EQ
|
|
|
|
Just orderByArg -> orderTrack orderByArg
|
2022-08-03 17:18:43 +03:00
|
|
|
limitFunction = maybe Hasura.Prelude.id take ta_limit
|
2022-03-17 23:53:56 +03:00
|
|
|
pure $
|
|
|
|
tracks
|
|
|
|
& filter filterFunction
|
|
|
|
& sortBy orderByFunction
|
|
|
|
& limitFunction
|
|
|
|
& map mkTrack
|
|
|
|
-- Returns True iif the given track matches the given boolean expression.
|
|
|
|
matchTrack trackInfo@(trackId, trackTitle, maybeAlbumId) (LHSHasuraTrackBoolExp {..}) =
|
|
|
|
and
|
|
|
|
[ maybe True (all (matchTrack trackInfo)) tbe__and,
|
|
|
|
maybe True (any (matchTrack trackInfo)) tbe__or,
|
|
|
|
maybe True (not . matchTrack trackInfo) tbe__not,
|
|
|
|
maybe True (matchInt trackId) tbe_id,
|
|
|
|
maybe True (matchString trackTitle) tbe_title,
|
|
|
|
maybe True (matchMaybeInt maybeAlbumId) tbe_album_id
|
|
|
|
]
|
|
|
|
matchInt intField IntCompExp {..} = Just intField == _eq
|
|
|
|
matchString stringField StringCompExp {..} = Just stringField == _eq
|
|
|
|
matchMaybeInt maybeIntField IntCompExp {..} = maybeIntField == _eq
|
|
|
|
-- Returns an ordering between the two given tracks.
|
|
|
|
orderTrack
|
|
|
|
orderByList
|
|
|
|
(trackId1, trackTitle1, trackAlbumId1)
|
|
|
|
(trackId2, trackTitle2, trackAlbumId2) =
|
|
|
|
flip foldMap orderByList \LHSHasuraTrackOrderBy {..} ->
|
|
|
|
if
|
|
|
|
| Just idOrder <- tob_id -> case idOrder of
|
|
|
|
Asc -> compare trackId1 trackId2
|
|
|
|
Desc -> compare trackId2 trackId1
|
|
|
|
| Just titleOrder <- tob_title -> case titleOrder of
|
|
|
|
Asc -> compare trackTitle1 trackTitle2
|
|
|
|
Desc -> compare trackTitle2 trackTitle1
|
|
|
|
| Just albumIdOrder <- tob_album_id ->
|
|
|
|
compareWithNullLast albumIdOrder trackAlbumId1 trackAlbumId2
|
|
|
|
| otherwise ->
|
|
|
|
error "empty track_order object"
|
|
|
|
compareWithNullLast Desc x1 x2 = compareWithNullLast Asc x2 x1
|
|
|
|
compareWithNullLast Asc Nothing Nothing = EQ
|
|
|
|
compareWithNullLast Asc (Just _) Nothing = LT
|
|
|
|
compareWithNullLast Asc Nothing (Just _) = GT
|
|
|
|
compareWithNullLast Asc (Just x1) (Just x2) = compare x1 x2
|
|
|
|
tracks =
|
|
|
|
[ (1, "track1_album1", Just 1),
|
|
|
|
(2, "track2_album1", Just 1),
|
|
|
|
(3, "track3_album1", Just 1),
|
|
|
|
(4, "track1_album2", Just 2),
|
|
|
|
(5, "track2_album2", Just 2),
|
|
|
|
(6, "track1_album3", Just 3),
|
|
|
|
(7, "track2_album3", Just 3),
|
|
|
|
(8, "track_no_album", Nothing)
|
|
|
|
]
|
|
|
|
mkTrack (trackId, title, albumId) =
|
|
|
|
LHSHasuraTrack
|
|
|
|
{ t_id = pure $ Just trackId,
|
|
|
|
t_title = pure $ Just title,
|
|
|
|
t_album_id = pure albumId
|
|
|
|
}
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsRemoteServerSetup :: (TestEnvironment, Maybe Server) -> IO ()
|
|
|
|
lhsRemoteServerSetup (testEnvironment, maybeRemoteServer) = case maybeRemoteServer of
|
|
|
|
Nothing -> error "XToDBObjectRelationshipSpec: remote server local testEnvironment did not succesfully create a server"
|
2022-03-17 23:53:56 +03:00
|
|
|
Just remoteServer -> do
|
|
|
|
let remoteSchemaEndpoint = GraphqlEngine.serverUrl remoteServer ++ "/graphql"
|
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-03-17 23:53:56 +03:00
|
|
|
[yaml|
|
|
|
|
type: bulk
|
|
|
|
args:
|
|
|
|
- type: add_remote_schema
|
|
|
|
args:
|
|
|
|
name: source
|
|
|
|
definition:
|
|
|
|
url: *remoteSchemaEndpoint
|
|
|
|
- type: create_remote_schema_remote_relationship
|
|
|
|
args:
|
|
|
|
remote_schema: source
|
|
|
|
type_name: hasura_track
|
|
|
|
name: album
|
|
|
|
definition:
|
|
|
|
to_remote_schema:
|
|
|
|
remote_schema: target
|
|
|
|
lhs_fields: [album_id]
|
|
|
|
remote_field:
|
|
|
|
album:
|
|
|
|
arguments:
|
|
|
|
album_id: $album_id
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
lhsRemoteServerTeardown :: (TestEnvironment, Maybe Server) -> IO ()
|
2022-03-17 23:53:56 +03:00
|
|
|
lhsRemoteServerTeardown (_, maybeServer) = traverse_ stopServer maybeServer
|
|
|
|
|
2022-02-18 16:35:32 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- RHS Remote Server
|
|
|
|
|
|
|
|
[gqlDocument|
|
|
|
|
|
|
|
|
type Album {
|
|
|
|
id: Int!
|
|
|
|
title: String!
|
|
|
|
artist_id: Int
|
|
|
|
}
|
|
|
|
|
|
|
|
type Query {
|
|
|
|
album(album_id: Int!): Album
|
|
|
|
}
|
|
|
|
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
rhsRemoteServerMkLocalTestEnvironment :: TestEnvironment -> IO Server
|
|
|
|
rhsRemoteServerMkLocalTestEnvironment _ =
|
2022-02-21 20:05:09 +03:00
|
|
|
RemoteServer.run $
|
|
|
|
RemoteServer.generateQueryInterpreter (Query {album})
|
2022-02-18 16:35:32 +03:00
|
|
|
where
|
|
|
|
albums =
|
|
|
|
[ (1, ("album1_artist1", Just 1)),
|
|
|
|
(2, ("album2_artist1", Just 1)),
|
|
|
|
(3, ("album3_artist2", Just 2))
|
|
|
|
]
|
2022-03-10 14:18:13 +03:00
|
|
|
album (Arg albumId) = pure $ mkAlbum albumId <$> lookup albumId albums
|
2022-02-18 16:35:32 +03:00
|
|
|
mkAlbum albumId (title, artist_id) =
|
|
|
|
Album
|
|
|
|
{ id = pure albumId,
|
|
|
|
title = pure title,
|
|
|
|
artist_id = pure artist_id
|
|
|
|
}
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
rhsRemoteSchemaSetup :: (TestEnvironment, Server) -> IO ()
|
|
|
|
rhsRemoteSchemaSetup (testEnvironment, remoteServer) = do
|
2022-02-21 20:05:09 +03:00
|
|
|
let remoteSchemaEndpoint = GraphqlEngine.serverUrl remoteServer ++ "/graphql"
|
|
|
|
GraphqlEngine.postMetadata_
|
2022-04-20 20:15:42 +03:00
|
|
|
testEnvironment
|
2022-02-21 20:05:09 +03:00
|
|
|
[yaml|
|
|
|
|
type: add_remote_schema
|
|
|
|
args:
|
|
|
|
name: target
|
|
|
|
definition:
|
|
|
|
url: *remoteSchemaEndpoint
|
|
|
|
|]
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
rhsRemoteSchemaTeardown :: (TestEnvironment, Server) -> IO ()
|
2022-03-17 23:53:56 +03:00
|
|
|
rhsRemoteSchemaTeardown (_, server) = stopServer server
|
2022-02-18 16:35:32 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Tests
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
tests :: Context.Options -> SpecWith (TestEnvironment, LocalTestTestEnvironment)
|
2022-03-17 23:53:56 +03:00
|
|
|
tests opts = describe "remote-schema-relationship" do
|
2022-02-18 16:35:32 +03:00
|
|
|
schemaTests opts
|
|
|
|
executionTests opts
|
|
|
|
|
|
|
|
-- | Basic queries using *-to-DB joins
|
2022-04-20 20:15:42 +03:00
|
|
|
executionTests :: Context.Options -> SpecWith (TestEnvironment, LocalTestTestEnvironment)
|
2022-03-17 23:53:56 +03:00
|
|
|
executionTests opts = describe "execution" do
|
2022-02-18 16:35:32 +03:00
|
|
|
-- fetches the relationship data
|
2022-04-20 20:15:42 +03:00
|
|
|
it "related-data" \(testEnvironment, _) -> do
|
2022-02-18 16:35:32 +03:00
|
|
|
let query =
|
|
|
|
[graphql|
|
|
|
|
query {
|
|
|
|
track: hasura_track(where: {title: {_eq: "track1_album1"}}) {
|
|
|
|
title
|
|
|
|
album {
|
|
|
|
title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|]
|
|
|
|
expectedResponse =
|
|
|
|
[yaml|
|
|
|
|
data:
|
|
|
|
track:
|
|
|
|
- title: "track1_album1"
|
|
|
|
album:
|
|
|
|
title: album1_artist1
|
|
|
|
|]
|
|
|
|
shouldReturnYaml
|
|
|
|
opts
|
2022-04-20 20:15:42 +03:00
|
|
|
(GraphqlEngine.postGraphql testEnvironment query)
|
2022-02-18 16:35:32 +03:00
|
|
|
expectedResponse
|
|
|
|
|
|
|
|
-- when any of the join columns are null, the relationship should be null
|
2022-04-20 20:15:42 +03:00
|
|
|
it "related-data-null" \(testEnvironment, _) -> do
|
2022-02-18 16:35:32 +03:00
|
|
|
let query =
|
|
|
|
[graphql|
|
|
|
|
query {
|
|
|
|
track: hasura_track(where: {title: {_eq: "track_no_album"}}) {
|
|
|
|
title
|
|
|
|
album {
|
|
|
|
title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|]
|
|
|
|
expectedResponse =
|
|
|
|
[yaml|
|
|
|
|
data:
|
|
|
|
track:
|
|
|
|
- title: "track_no_album"
|
|
|
|
album: null
|
|
|
|
|]
|
|
|
|
shouldReturnYaml
|
|
|
|
opts
|
2022-04-20 20:15:42 +03:00
|
|
|
(GraphqlEngine.postGraphql testEnvironment query)
|
2022-02-18 16:35:32 +03:00
|
|
|
expectedResponse
|
|
|
|
|
|
|
|
-- when the lhs response has both null and non-null values for join columns
|
2022-04-20 20:15:42 +03:00
|
|
|
it "related-data-non-null-and-null" \(testEnvironment, _) -> do
|
2022-02-18 16:35:32 +03:00
|
|
|
let query =
|
|
|
|
[graphql|
|
|
|
|
query {
|
|
|
|
track: hasura_track(
|
|
|
|
where: {
|
|
|
|
_or: [
|
|
|
|
{title: {_eq: "track1_album1"}},
|
|
|
|
{title: {_eq: "track_no_album"}}
|
|
|
|
]
|
|
|
|
},
|
2022-03-17 23:53:56 +03:00
|
|
|
order_by: [{id: asc}]
|
2022-02-18 16:35:32 +03:00
|
|
|
) {
|
|
|
|
title
|
|
|
|
album {
|
|
|
|
title
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|]
|
|
|
|
expectedResponse =
|
|
|
|
[yaml|
|
|
|
|
data:
|
|
|
|
track:
|
|
|
|
- title: "track1_album1"
|
|
|
|
album:
|
|
|
|
title: album1_artist1
|
|
|
|
- title: "track_no_album"
|
|
|
|
album: null
|
|
|
|
|]
|
|
|
|
shouldReturnYaml
|
|
|
|
opts
|
2022-04-20 20:15:42 +03:00
|
|
|
(GraphqlEngine.postGraphql testEnvironment query)
|
2022-02-18 16:35:32 +03:00
|
|
|
expectedResponse
|
|
|
|
|
2022-04-20 20:15:42 +03:00
|
|
|
schemaTests :: Context.Options -> SpecWith (TestEnvironment, LocalTestTestEnvironment)
|
2022-02-18 16:35:32 +03:00
|
|
|
schemaTests opts =
|
|
|
|
-- we use an introspection query to check:
|
|
|
|
-- 1. a field 'album' is added to the track table
|
|
|
|
-- 1. track's where clause does not have 'album' field
|
|
|
|
-- 2. track's order_by clause does nat have 'album' field
|
2022-04-20 20:15:42 +03:00
|
|
|
it "graphql-schema" \(testEnvironment, _) -> do
|
2022-02-18 16:35:32 +03:00
|
|
|
let query =
|
|
|
|
[graphql|
|
|
|
|
query {
|
|
|
|
track_fields: __type(name: "hasura_track") {
|
|
|
|
fields {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
track_where_exp_fields: __type(name: "hasura_track_bool_exp") {
|
|
|
|
inputFields {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
track_order_by_exp_fields: __type(name: "hasura_track_order_by") {
|
|
|
|
inputFields {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|]
|
|
|
|
expectedResponse =
|
|
|
|
[yaml|
|
|
|
|
data:
|
|
|
|
track_fields:
|
|
|
|
fields:
|
|
|
|
- name: album
|
|
|
|
- name: album_id
|
|
|
|
- name: id
|
|
|
|
- name: title
|
|
|
|
track_where_exp_fields:
|
|
|
|
inputFields:
|
|
|
|
- name: _and
|
|
|
|
- name: _not
|
|
|
|
- name: _or
|
|
|
|
- name: album_id
|
|
|
|
- name: id
|
|
|
|
- name: title
|
|
|
|
track_order_by_exp_fields:
|
|
|
|
inputFields:
|
|
|
|
- name: album_id
|
|
|
|
- name: id
|
|
|
|
- name: title
|
|
|
|
|]
|
|
|
|
shouldReturnYaml
|
|
|
|
opts
|
2022-04-20 20:15:42 +03:00
|
|
|
(GraphqlEngine.postGraphql testEnvironment query)
|
2022-02-18 16:35:32 +03:00
|
|
|
expectedResponse
|