mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-16 18:42:30 +03:00
d54bb30d3b
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6061 Co-authored-by: Vishnu Bharathi <4211715+scriptnull@users.noreply.github.com> GitOrigin-RevId: 855d96378030f4e01b0c74b00e20e592e51e7a49
265 lines
13 KiB
Haskell
265 lines
13 KiB
Haskell
module Test.QuerySpec.OrderBySpec (spec) where
|
|
|
|
import Control.Arrow ((>>>))
|
|
import Control.Lens (ix, (&), (.~), (?~), (^.), (^?), _1, _2, _3, _Just)
|
|
import Control.Monad (when)
|
|
import Data.Aeson (Value (..))
|
|
import Data.HashMap.Strict (HashMap)
|
|
import Data.HashMap.Strict qualified as HashMap
|
|
import Data.List (sortOn)
|
|
import Data.List.NonEmpty (NonEmpty (..))
|
|
import Data.List.NonEmpty qualified as NonEmpty
|
|
import Data.Maybe (isJust)
|
|
import Data.Ord (Down (..))
|
|
import Hasura.Backends.DataConnector.API
|
|
import Servant.API (NamedRoutes)
|
|
import Servant.Client (Client)
|
|
import Test.Data (TestData (..), guardedQuery)
|
|
import Test.Data qualified as Data
|
|
import Test.Expectations (jsonShouldBe, rowsShouldBe)
|
|
import Test.Hspec (Spec, describe, it)
|
|
import Prelude
|
|
|
|
spec :: TestData -> Client IO (NamedRoutes Routes) -> SourceName -> Config -> Capabilities -> Spec
|
|
spec TestData {..} api sourceName config Capabilities {..} = describe "Order By in Queries" $ do
|
|
it "can order results in ascending order" $ do
|
|
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Ascending :| []
|
|
let query = albumsQueryRequest & qrQuery . qOrderBy ?~ orderBy
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let expectedAlbums = sortOn (^? Data.field "Title") _tdAlbumsRows
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedAlbums
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
|
|
it "can order results in descending order" $ do
|
|
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Descending :| []
|
|
let query = albumsQueryRequest & qrQuery . qOrderBy ?~ orderBy
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let expectedAlbums = sortOn (Down . (^? Data.field "Title")) _tdAlbumsRows
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedAlbums
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
|
|
it "can use multiple order by elements to order results" $ do
|
|
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "ArtistId" Ascending :| [_tdOrderByColumn [] "Title" Descending]
|
|
let query = albumsQueryRequest & qrQuery . qOrderBy ?~ orderBy
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let expectedAlbums =
|
|
sortOn (\album -> (album ^? Data.field "ArtistId", Down (album ^? Data.field "Title"))) _tdAlbumsRows
|
|
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedAlbums
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
|
|
when (isJust _cRelationships) . describe "involving relationships" $ do
|
|
it "can order results by a column in a related table" $ do
|
|
let orderByRelations = HashMap.fromList [(_tdArtistRelationshipName, OrderByRelation Nothing mempty)]
|
|
let orderBy = OrderBy orderByRelations $ _tdOrderByColumn [_tdArtistRelationshipName] "Name" Ascending :| []
|
|
let query =
|
|
albumsQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships .~ [Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships]
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let getRelatedArtist (album :: HashMap FieldName FieldValue) =
|
|
(album ^? Data.field "ArtistId" . Data._ColumnFieldNumber) >>= \artistId -> _tdArtistsRowsById ^? ix artistId
|
|
|
|
let expectedAlbums =
|
|
_tdAlbumsRows
|
|
& fmap (\album -> (album, getRelatedArtist album))
|
|
& sortOn ((^? _2 . _Just . Data.field "Name"))
|
|
& fmap fst
|
|
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedAlbums
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
|
|
it "can order results by a column in a related table where the related table is filtered" $ do
|
|
let artistTableFilter = ApplyBinaryComparisonOperator GreaterThan (_tdCurrentComparisonColumn "Name") (ScalarValue $ String "N")
|
|
let orderByRelations = HashMap.fromList [(_tdArtistRelationshipName, OrderByRelation (Just artistTableFilter) mempty)]
|
|
let orderBy = OrderBy orderByRelations $ _tdOrderByColumn [_tdArtistRelationshipName] "Name" Ascending :| []
|
|
let query =
|
|
albumsQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships .~ [Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships]
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let getRelatedArtist (album :: HashMap FieldName FieldValue) = do
|
|
artist <- (album ^? Data.field "ArtistId" . Data._ColumnFieldNumber) >>= \artistId -> _tdArtistsRowsById ^? ix artistId
|
|
if artist ^? Data.field "Name" . Data._ColumnFieldString > Just "N"
|
|
then pure artist
|
|
else Nothing
|
|
|
|
let expectedAlbums =
|
|
_tdAlbumsRows
|
|
& fmap (\album -> (album, getRelatedArtist album))
|
|
& sortOn ((^? _2 . _Just . Data.field "Name") >>> toNullsLastOrdering)
|
|
& fmap fst
|
|
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedAlbums
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
|
|
it "can order results by a column in a related table of a related table" $ do
|
|
let orderByRelations =
|
|
HashMap.fromList
|
|
[ ( _tdAlbumRelationshipName,
|
|
OrderByRelation
|
|
Nothing
|
|
( HashMap.fromList
|
|
[ ( _tdArtistRelationshipName,
|
|
OrderByRelation
|
|
Nothing
|
|
mempty
|
|
)
|
|
]
|
|
)
|
|
)
|
|
]
|
|
let orderBy =
|
|
OrderBy orderByRelations $
|
|
NonEmpty.fromList
|
|
[ _tdOrderByColumn [_tdAlbumRelationshipName, _tdArtistRelationshipName] "Name" Descending,
|
|
_tdOrderByColumn [] "Name" Ascending
|
|
]
|
|
let query =
|
|
tracksQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships
|
|
.~ [ Data.onlyKeepRelationships [_tdAlbumRelationshipName] _tdTracksTableRelationships,
|
|
Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships
|
|
]
|
|
receivedTracks <- guardedQuery api sourceName config query
|
|
|
|
let getRelatedArtist (track :: HashMap FieldName FieldValue) = do
|
|
albumId <- track ^? Data.field "AlbumId" . Data._ColumnFieldNumber
|
|
album <- _tdAlbumsRowsById ^? ix albumId
|
|
artistId <- album ^? Data.field "ArtistId" . Data._ColumnFieldNumber
|
|
_tdArtistsRowsById ^? ix artistId
|
|
|
|
let expectedTracks =
|
|
_tdTracksRows
|
|
& fmap (\track -> (Data.filterColumnsByQueryFields (_qrQuery tracksQueryRequest) track, getRelatedArtist track, track ^? Data.field "Name"))
|
|
& sortOn (\row -> (Down (row ^? _2 . _Just . Data.field "Name"), row ^. _3))
|
|
& fmap (^. _1)
|
|
|
|
Data.responseRows receivedTracks `rowsShouldBe` expectedTracks
|
|
_qrAggregates receivedTracks `jsonShouldBe` Nothing
|
|
|
|
it "can order results by an aggregate of a related table" $ do
|
|
let orderByRelations = HashMap.fromList [(_tdAlbumsRelationshipName, OrderByRelation Nothing mempty)]
|
|
let orderBy = OrderBy orderByRelations $ OrderByElement [_tdAlbumsRelationshipName] OrderByStarCountAggregate Descending :| []
|
|
let query =
|
|
artistsQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships .~ [Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships]
|
|
receivedArtists <- guardedQuery api sourceName config query
|
|
|
|
let getAlbumsCount (artist :: HashMap FieldName FieldValue) = do
|
|
artistId <- artist ^? Data.field "ArtistId" . Data._ColumnFieldNumber
|
|
let albums = filter (\album -> album ^? Data.field "ArtistId" . Data._ColumnFieldNumber == Just artistId) _tdAlbumsRows
|
|
pure $ length albums
|
|
|
|
let expectedArtists =
|
|
_tdArtistsRows
|
|
& fmap (\artist -> (artist, getAlbumsCount artist))
|
|
& sortOn (Down . (^. _2))
|
|
& fmap fst
|
|
|
|
Data.responseRows receivedArtists `rowsShouldBe` expectedArtists
|
|
_qrAggregates receivedArtists `jsonShouldBe` Nothing
|
|
|
|
it "can order results by an aggregate of a related table where the related table is filtered" $ do
|
|
let albumTableFilter = ApplyBinaryComparisonOperator GreaterThan (_tdCurrentComparisonColumn "Title") (ScalarValue $ String "N")
|
|
let orderByRelations = HashMap.fromList [(_tdAlbumsRelationshipName, OrderByRelation (Just albumTableFilter) mempty)]
|
|
let orderBy = OrderBy orderByRelations $ OrderByElement [_tdAlbumsRelationshipName] OrderByStarCountAggregate Descending :| []
|
|
let query =
|
|
artistsQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships .~ [Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships]
|
|
receivedArtists <- guardedQuery api sourceName config query
|
|
|
|
let getAlbumsCount (artist :: HashMap FieldName FieldValue) = do
|
|
artistId <- artist ^? Data.field "ArtistId" . Data._ColumnFieldNumber
|
|
let albums = filter (\album -> album ^? Data.field "ArtistId" . Data._ColumnFieldNumber == Just artistId && album ^? Data.field "Title" . Data._ColumnFieldString > Just "N") _tdAlbumsRows
|
|
pure $ length albums
|
|
|
|
let expectedArtists =
|
|
_tdArtistsRows
|
|
& fmap (\artist -> (artist, getAlbumsCount artist))
|
|
& sortOn (Down . (^. _2))
|
|
& fmap fst
|
|
|
|
Data.responseRows receivedArtists `rowsShouldBe` expectedArtists
|
|
_qrAggregates receivedArtists `jsonShouldBe` Nothing
|
|
|
|
it "can order results by an aggregate of a related table's related table" $ do
|
|
let orderByRelations =
|
|
HashMap.fromList
|
|
[ ( _tdArtistRelationshipName,
|
|
OrderByRelation
|
|
Nothing
|
|
( HashMap.fromList
|
|
[ ( _tdAlbumsRelationshipName,
|
|
OrderByRelation
|
|
Nothing
|
|
mempty
|
|
)
|
|
]
|
|
)
|
|
)
|
|
]
|
|
let orderBy =
|
|
OrderBy orderByRelations $
|
|
NonEmpty.fromList
|
|
[ OrderByElement [_tdArtistRelationshipName, _tdAlbumsRelationshipName] OrderByStarCountAggregate Descending,
|
|
_tdOrderByColumn [] "Title" Ascending
|
|
]
|
|
let query =
|
|
albumsQueryRequest
|
|
& qrQuery . qOrderBy ?~ orderBy
|
|
& qrTableRelationships
|
|
.~ [ Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships,
|
|
Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships
|
|
]
|
|
receivedAlbums <- guardedQuery api sourceName config query
|
|
|
|
let getTotalArtistAlbumsCount (album :: HashMap FieldName FieldValue) = do
|
|
artistId <- album ^? Data.field "ArtistId" . Data._ColumnFieldNumber
|
|
let albums = filter (\album' -> album' ^? Data.field "ArtistId" . Data._ColumnFieldNumber == Just artistId) _tdAlbumsRows
|
|
pure $ length albums
|
|
|
|
let expectedArtists =
|
|
_tdAlbumsRows
|
|
& fmap (\album -> (album, getTotalArtistAlbumsCount album, album ^? Data.field "Title"))
|
|
& sortOn (\row -> (Down (row ^. _2), (row ^. _3)))
|
|
& fmap (^. _1)
|
|
|
|
Data.responseRows receivedAlbums `rowsShouldBe` expectedArtists
|
|
_qrAggregates receivedAlbums `jsonShouldBe` Nothing
|
|
where
|
|
albumsQueryRequest :: QueryRequest
|
|
albumsQueryRequest =
|
|
let fields = Data.mkFieldsMap [("AlbumId", _tdColumnField "AlbumId"), ("ArtistId", _tdColumnField "ArtistId"), ("Title", _tdColumnField "Title")]
|
|
query = Data.emptyQuery & qFields ?~ fields
|
|
in QueryRequest _tdAlbumsTableName [] query
|
|
|
|
artistsQueryRequest :: QueryRequest
|
|
artistsQueryRequest =
|
|
let fields = Data.mkFieldsMap [("ArtistId", _tdColumnField "ArtistId"), ("Name", _tdColumnField "Name")]
|
|
query = Data.emptyQuery & qFields ?~ fields
|
|
in QueryRequest _tdArtistsTableName [] query
|
|
|
|
tracksQueryRequest :: QueryRequest
|
|
tracksQueryRequest =
|
|
let fields = Data.mkFieldsMap [("TrackId", _tdColumnField "TrackId"), ("Name", _tdColumnField "Name")]
|
|
query = Data.emptyQuery & qFields ?~ fields
|
|
in QueryRequest _tdTracksTableName [] query
|
|
|
|
data NullableOrdered a
|
|
= NullFirst
|
|
| Some a
|
|
| NullLast
|
|
deriving stock (Eq, Ord, Show)
|
|
|
|
toNullsLastOrdering :: Maybe a -> NullableOrdered a
|
|
toNullsLastOrdering = maybe NullLast Some
|