mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
Fix "limit" from permissions being incorrectly applied to aggregates in Data Connectors
[GDC-1064]: https://hasurahq.atlassian.net/browse/GDC-1064?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8345 GitOrigin-RevId: 9ef91944ba6408e1030a47de58f3271145433ee2
This commit is contained in:
parent
6e1ebd6a4b
commit
e9c697aaa9
@ -473,7 +473,125 @@ The rows returned by the query must be put into the `rows` property array in the
|
||||
|
||||
#### Pagination
|
||||
|
||||
If the GraphQL query contains pagination information, then the `limit` and `offset` fields may be set to integer values, indicating the number of rows to return, and the index of the first row to return, respectively.
|
||||
There are three properties that are used to control pagination of queried data:
|
||||
|
||||
* `aggregates_limit`: The maximum number of rows to consider in aggregations calculated and returned in the `aggregrates` property. `aggregates_limit` does not influence the rows returned in the `rows` property. It will only be used if there are aggregates in the query.
|
||||
* `limit`: The maximum number of rows to return from a query in the `rows` property. `limit` does not influence the rows considered by aggregations.
|
||||
* `offset`: The index of the first row to return. This affects the rows returned, and also the rows considered by aggregations.
|
||||
|
||||
`limit` and `aggregates_limit` are set when the user uses a `limit` parameter in their GraphQL query. This restricts the dataset considered when returning rows as well as calculating aggregates.
|
||||
HGE also has a row limit setting in a table's select permissions. This row limit will be used in the `limit` property if it is specified or if it is smaller than the limit specified in the GraphQL query itself.
|
||||
|
||||
To illustrate the difference between `limit` and `aggregates_limit`, consider this GraphQL query, where a row limit of `2` has been placed on the Artist table in its select permissions.
|
||||
|
||||
```graphql
|
||||
query ArtistsQuery {
|
||||
Artist_aggregate {
|
||||
aggregate {
|
||||
count
|
||||
}
|
||||
nodes {
|
||||
Name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This would produce the following agent query request JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"table": ["Artist"],
|
||||
"table_relationships": [],
|
||||
"query": {
|
||||
"aggregates_limit": null,
|
||||
"limit": 2,
|
||||
"offset": null,
|
||||
"aggregates": {
|
||||
"aggregate_count": {
|
||||
"type": "star_count"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"nodes_Name": {
|
||||
"type": "column",
|
||||
"column": "Name",
|
||||
"column_type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The expected response to that query request would be the following JSON. Note that the row count has counted all rows (since `aggregates_limit` was null), but query has only returned the maximum number of rows as specified by the `limit` property: `2`.
|
||||
```json
|
||||
{
|
||||
"aggregates": {
|
||||
"aggregate_count": 275
|
||||
},
|
||||
"rows":[
|
||||
{ "nodes_Name": "AC/DC" },
|
||||
{ "nodes_Name": "Accept" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
By comparison, if we added a limit to our GraphQL query:
|
||||
|
||||
```graphql
|
||||
query ArtistsQuery {
|
||||
Artist_aggregate(limit: 5) {
|
||||
aggregate {
|
||||
count
|
||||
}
|
||||
nodes {
|
||||
Name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This would produce the following agent query request JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"table": ["Artist"],
|
||||
"table_relationships": [],
|
||||
"query": {
|
||||
"aggregates_limit": 5,
|
||||
"limit": 2,
|
||||
"offset": null,
|
||||
"aggregates": {
|
||||
"aggregate_count": {
|
||||
"type": "star_count"
|
||||
}
|
||||
},
|
||||
"fields": {
|
||||
"nodes_Name": {
|
||||
"type": "column",
|
||||
"column": "Name",
|
||||
"column_type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We would expect the following result:
|
||||
|
||||
```json
|
||||
{
|
||||
"aggregates": {
|
||||
"aggregate_count": 5
|
||||
},
|
||||
"rows":[
|
||||
{ "nodes_Name": "AC/DC" },
|
||||
{ "nodes_Name": "Accept" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Note that the row count aggregation has been limited to `5` because `aggregates_limit` was `5`, and the rows returned were limited by the value of `limit`: `2`.
|
||||
|
||||
#### Filters
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.26.0",
|
||||
"version": "0.27.0",
|
||||
"description": "Hasura GraphQL Engine Data Connector Agent API types",
|
||||
"author": "Hasura (https://github.com/hasura/graphql-engine)",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -1305,6 +1305,13 @@
|
||||
"nullable": true,
|
||||
"type": "object"
|
||||
},
|
||||
"aggregates_limit": {
|
||||
"description": "Optionally limit the maximum number of rows considered while applying aggregations. This limit does not apply to returned rows.",
|
||||
"maximum": 9223372036854776000,
|
||||
"minimum": -9223372036854776000,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
},
|
||||
"fields": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/Field"
|
||||
@ -1314,14 +1321,14 @@
|
||||
"type": "object"
|
||||
},
|
||||
"limit": {
|
||||
"description": "Optionally limit to N results",
|
||||
"description": "Optionally limit the maximum number of returned rows. This limit does not apply to records considered while apply aggregations.",
|
||||
"maximum": 9223372036854776000,
|
||||
"minimum": -9223372036854776000,
|
||||
"nullable": true,
|
||||
"type": "number"
|
||||
},
|
||||
"offset": {
|
||||
"description": "Optionally offset from the Nth result",
|
||||
"description": "Optionally offset from the Nth result. This applies to both row and aggregation results.",
|
||||
"maximum": 9223372036854776000,
|
||||
"minimum": -9223372036854776000,
|
||||
"nullable": true,
|
||||
|
@ -12,16 +12,20 @@ export type Query = {
|
||||
* Aggregate fields of the query
|
||||
*/
|
||||
aggregates?: Record<string, Aggregate> | null;
|
||||
/**
|
||||
* Optionally limit the maximum number of rows considered while applying aggregations. This limit does not apply to returned rows.
|
||||
*/
|
||||
aggregates_limit?: number | null;
|
||||
/**
|
||||
* Fields of the query
|
||||
*/
|
||||
fields?: Record<string, Field> | null;
|
||||
/**
|
||||
* Optionally limit to N results
|
||||
* Optionally limit the maximum number of returned rows. This limit does not apply to records considered while apply aggregations.
|
||||
*/
|
||||
limit?: number | null;
|
||||
/**
|
||||
* Optionally offset from the Nth result
|
||||
* Optionally offset from the Nth result. This applies to both row and aggregation results.
|
||||
*/
|
||||
offset?: number | null;
|
||||
order_by?: OrderBy;
|
||||
|
10
dc-agents/package-lock.json
generated
10
dc-agents/package-lock.json
generated
@ -24,7 +24,7 @@
|
||||
},
|
||||
"dc-api-types": {
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.26.0",
|
||||
"version": "0.27.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
@ -2227,7 +2227,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -2547,7 +2547,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify": "^4.13.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -2868,7 +2868,7 @@
|
||||
"version": "file:reference",
|
||||
"requires": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
@ -3080,7 +3080,7 @@
|
||||
"version": "file:sqlite",
|
||||
"requires": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/sqlite3": "^3.1.8",
|
||||
|
4
dc-agents/reference/package-lock.json
generated
4
dc-agents/reference/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -52,7 +52,7 @@
|
||||
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.26.0",
|
||||
"version": "0.27.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
|
@ -578,12 +578,22 @@ export const queryData = (getTable: (tableName: TableName) => Record<string, Raw
|
||||
|
||||
const filteredRows = filterIterable(rows, makeFilterPredicate(query.where ?? null, getComparisonColumnValue, performExistsSubquery));
|
||||
const sortedRows = query.order_by ? sortRows(Array.from(filteredRows), query.order_by, getOrderByElementValue) : filteredRows;
|
||||
const paginatedRows = Array.from(paginateRows(sortedRows, query.offset ?? null, query.limit ?? null));
|
||||
|
||||
// Get the smallest set of rows required _for both_ row results and aggregation result
|
||||
const aggregatesLimit = query.aggregates_limit ?? null;
|
||||
const rowLimit = query.limit ?? null;
|
||||
const largestLimit = aggregatesLimit !== null && rowLimit !== null ? Math.max(aggregatesLimit, rowLimit) : null;
|
||||
const largestPageOfRows = Array.from(paginateRows(sortedRows, query.offset ?? null, largestLimit));
|
||||
|
||||
// Limit the set of input rows to appropriate size for row results and aggregation results
|
||||
const paginatedRows = rowLimit != null ? largestPageOfRows.slice(0, rowLimit) : largestPageOfRows;
|
||||
const paginatedRowsForAggregation = aggregatesLimit != null ? largestPageOfRows.slice(0, aggregatesLimit) : largestPageOfRows;
|
||||
|
||||
const projectedRows = query.fields
|
||||
? paginatedRows.map(projectRow(query.fields, findRelationship, performNewQuery))
|
||||
: null;
|
||||
const calculatedAggregates = query.aggregates
|
||||
? calculateAggregates(paginatedRows, query.aggregates)
|
||||
? calculateAggregates(paginatedRowsForAggregation, query.aggregates)
|
||||
: null;
|
||||
return {
|
||||
aggregates: calculatedAggregates,
|
||||
|
4
dc-agents/sqlite/package-lock.json
generated
4
dc-agents/sqlite/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify": "^4.13.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -57,7 +57,7 @@
|
||||
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.26.0",
|
||||
"version": "0.27.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.26.0",
|
||||
"@hasura/dc-api-types": "0.27.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"fastify": "^4.13.0",
|
||||
"nanoid": "^3.3.4",
|
||||
|
@ -23,9 +23,6 @@ import {
|
||||
OrderByRelation,
|
||||
OrderByElement,
|
||||
OrderByTarget,
|
||||
Query,
|
||||
ColumnFieldValue,
|
||||
NullColumnFieldValue,
|
||||
ScalarValue,
|
||||
} from "@hasura/dc-api-types";
|
||||
import { customAlphabet } from "nanoid";
|
||||
@ -335,12 +332,13 @@ function table_query(
|
||||
fields: Fields | null,
|
||||
aggregates: Aggregates | null,
|
||||
wWhere: Expression | null,
|
||||
aggregatesLimit: number | null,
|
||||
wLimit: number | null,
|
||||
wOffset: number | null,
|
||||
wOrder: OrderBy | null,
|
||||
): string {
|
||||
const tableAlias = generateTableAlias(tableName);
|
||||
const aggregateSelect = aggregates ? [aggregates_query(ts, tableName, joinInfo, aggregates, wWhere, wLimit, wOffset, wOrder)] : [];
|
||||
const aggregateSelect = aggregates ? [aggregates_query(ts, tableName, joinInfo, aggregates, wWhere, aggregatesLimit, wOffset, wOrder)] : [];
|
||||
// The use of the JSON function inside JSON_GROUP_ARRAY is necessary from SQLite 3.39.0 due to breaking changes in
|
||||
// SQLite. See https://sqlite.org/forum/forumpost/e3b101fb3234272b for more details. This approach still works fine
|
||||
// for older versions too.
|
||||
@ -374,10 +372,10 @@ function relationship(ts: TableRelationships[], r: Relationship, field: Relation
|
||||
|
||||
// We force a limit of 1 for object relationships in case the user has configured a manual
|
||||
// "object" relationship that accidentally actually is an array relationship
|
||||
const limit =
|
||||
const [limit, aggregatesLimit] =
|
||||
r.relationship_type === "object"
|
||||
? 1
|
||||
: coerceUndefinedToNull(field.query.limit);
|
||||
? [1, 1]
|
||||
: [coerceUndefinedToNull(field.query.limit), coerceUndefinedToNull(field.query.aggregates_limit)];
|
||||
|
||||
return tag("relationship", table_query(
|
||||
ts,
|
||||
@ -386,6 +384,7 @@ function relationship(ts: TableRelationships[], r: Relationship, field: Relation
|
||||
coerceUndefinedToNull(field.query.fields),
|
||||
coerceUndefinedToNull(field.query.aggregates),
|
||||
coerceUndefinedToNull(field.query.where),
|
||||
aggregatesLimit,
|
||||
limit,
|
||||
coerceUndefinedToNull(field.query.offset),
|
||||
coerceUndefinedToNull(field.query.order_by),
|
||||
@ -657,6 +656,7 @@ function query(request: QueryRequest): string {
|
||||
coerceUndefinedToNull(request.query.fields),
|
||||
coerceUndefinedToNull(request.query.aggregates),
|
||||
coerceUndefinedToNull(request.query.where),
|
||||
coerceUndefinedToNull(request.query.aggregates_limit),
|
||||
coerceUndefinedToNull(request.query.limit),
|
||||
coerceUndefinedToNull(request.query.offset),
|
||||
coerceUndefinedToNull(request.query.order_by),
|
||||
@ -740,6 +740,7 @@ function foreach_query(foreachIds: Record<string, ScalarValue>[], request: Query
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
return tag('foreach_query', `WITH ${escapeTableName(foreachTableName)} AS (${foreachIdsTableValue}) SELECT ${tableSubquery} AS data`);
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ library
|
||||
Test.DataConnector.MockAgent.OrderBySpec
|
||||
Test.DataConnector.MockAgent.QueryRelationshipsSpec
|
||||
Test.DataConnector.MockAgent.RemoteRelationshipsSpec
|
||||
Test.DataConnector.MockAgent.TestHelpers
|
||||
Test.DataConnector.MockAgent.TransformedConfigurationSpec
|
||||
Test.DataConnector.MockAgent.UpdateMutationsSpec
|
||||
Test.DataConnector.QuerySpec
|
||||
|
@ -4,6 +4,7 @@ module Test.DataConnector.MockAgent.AggregateQuerySpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
@ -18,6 +19,7 @@ import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Language.GraphQL.Draft.Syntax.QQ qualified as G
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -108,14 +110,14 @@ tests _opts = describe "Aggregate Query Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
|
||||
( API.FieldName "nodes_Albums",
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
|
||||
( "nodes_Albums",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")],
|
||||
[(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
|
||||
mkRowsQueryResponse
|
||||
[ [("nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")],
|
||||
[("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
|
||||
]
|
||||
)
|
||||
]
|
||||
@ -142,57 +144,40 @@ tests _opts = describe "Aggregate Query Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Artist" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Artist" :| []),
|
||||
mkQueryRequest
|
||||
(mkTableName "Artist")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
|
||||
("ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( "nodes_Albums",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Albums")
|
||||
( emptyQuery
|
||||
& API.qFields ?~ mkFieldsMap [("nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Artist",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Albums",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Album" :| []),
|
||||
{ _rTargetTable = mkTableName "Album",
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
|
||||
(API.FieldName "ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( API.FieldName "nodes_Albums",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Albums")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
)
|
||||
)
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "works with multiple aggregate fields and through array relations" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -224,28 +209,28 @@ tests _opts = describe "Aggregate Query Tests" $ do
|
||||
}
|
||||
|]
|
||||
let aggregates =
|
||||
[ (API.FieldName "counts_count", Aeson.Number 2),
|
||||
(API.FieldName "counts_uniqueBillingCountries", Aeson.Number 2),
|
||||
(API.FieldName "ids_minimum_Id", Aeson.Number 1),
|
||||
(API.FieldName "ids_max_InvoiceId", Aeson.Number 2)
|
||||
[ ("counts_count", Aeson.Number 2),
|
||||
("counts_uniqueBillingCountries", Aeson.Number 2),
|
||||
("ids_minimum_Id", Aeson.Number 1),
|
||||
("ids_max_InvoiceId", Aeson.Number 2)
|
||||
]
|
||||
rows =
|
||||
[ [ ( API.FieldName "nodes_Lines",
|
||||
[ [ ( "nodes_Lines",
|
||||
API.mkRelationshipFieldValue $
|
||||
aggregatesResponse
|
||||
[ (API.FieldName "aggregate_count", Aeson.Number 2)
|
||||
mkAggregatesQueryResponse
|
||||
[ ("aggregate_count", Aeson.Number 2)
|
||||
]
|
||||
)
|
||||
],
|
||||
[ ( API.FieldName "nodes_Lines",
|
||||
[ ( "nodes_Lines",
|
||||
API.mkRelationshipFieldValue $
|
||||
aggregatesResponse
|
||||
[ (API.FieldName "aggregate_count", Aeson.Number 4)
|
||||
mkAggregatesQueryResponse
|
||||
[ ("aggregate_count", Aeson.Number 4)
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (aggregatesAndRowsResponse aggregates rows)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkQueryResponse rows aggregates)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -273,71 +258,46 @@ tests _opts = describe "Aggregate Query Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Invoice" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Invoice" :| []),
|
||||
mkQueryRequest
|
||||
(mkTableName "Invoice")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ( "nodes_Lines",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "InvoiceLines")
|
||||
( emptyQuery & API.qAggregates ?~ mkFieldsMap [("aggregate_count", API.StarCount)]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
& API.qAggregates
|
||||
?~ mkFieldsMap
|
||||
[ ("counts_count", API.StarCount),
|
||||
("counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
|
||||
("ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId") (API.ScalarType "number"))),
|
||||
("ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId") (API.ScalarType "number")))
|
||||
]
|
||||
& API.qLimit ?~ 2
|
||||
& API.qAggregatesLimit ?~ 2
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Invoice",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "InvoiceLines",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("InvoiceLine" :| []),
|
||||
{ _rTargetTable = mkTableName "InvoiceLine",
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "InvoiceId", API.ColumnName "InvoiceId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ ( API.FieldName "nodes_Lines",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "InvoiceLines")
|
||||
API.Query
|
||||
{ _qFields = Nothing,
|
||||
_qAggregates =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[(API.FieldName "aggregate_count", API.StarCount)],
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
],
|
||||
_qAggregates =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "counts_count", API.StarCount),
|
||||
(API.FieldName "counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
|
||||
(API.FieldName "ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId") (API.ScalarType "number"))),
|
||||
(API.FieldName "ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId") (API.ScalarType "number")))
|
||||
],
|
||||
_qLimit = Just 2,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
||||
aggregatesResponse :: [(API.FieldName, Aeson.Value)] -> API.QueryResponse
|
||||
aggregatesResponse aggregates = API.QueryResponse Nothing (Just $ HashMap.fromList aggregates)
|
||||
|
||||
aggregatesAndRowsResponse :: [(API.FieldName, Aeson.Value)] -> [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
aggregatesAndRowsResponse aggregates rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) (Just $ HashMap.fromList aggregates)
|
||||
|
||||
singleColumnAggregateMax :: API.ColumnName -> API.ScalarType -> API.SingleColumnAggregate
|
||||
singleColumnAggregateMax = API.SingleColumnAggregate $ API.SingleColumnAggregateFunction [G.name|max|]
|
||||
|
@ -6,9 +6,9 @@ module Test.DataConnector.MockAgent.BasicQuerySpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
|
||||
import Harness.Backend.DataConnector.Mock qualified as Mock
|
||||
@ -20,6 +20,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -73,19 +74,19 @@ sourceMetadata =
|
||||
configuration:
|
||||
custom_root_fields:
|
||||
select: artists
|
||||
select_by_pk: artists_by_pk
|
||||
column_config:
|
||||
ArtistId:
|
||||
custom_name: id
|
||||
Name:
|
||||
custom_name: name
|
||||
array_relationships:
|
||||
- name: albums
|
||||
using:
|
||||
manual_configuration:
|
||||
remote_table: [Album]
|
||||
column_mapping:
|
||||
ArtistId: ArtistId
|
||||
select_permissions:
|
||||
- role: *testRoleName
|
||||
permission:
|
||||
columns:
|
||||
- ArtistId
|
||||
- Name
|
||||
limit: 3
|
||||
filter: {}
|
||||
- table: [Employee]
|
||||
- table: [Customer]
|
||||
select_permissions:
|
||||
@ -118,9 +119,9 @@ tests _opts = describe "Basic Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
@ -138,25 +139,70 @@ tests _opts = describe "Basic Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "permissions-based row limits are applied" $ \_testEnv performGraphqlRequest -> do
|
||||
let headers = [("X-Hasura-Role", testRoleName)]
|
||||
let graphqlRequest =
|
||||
[graphql|
|
||||
query getArtists {
|
||||
artists(limit: 5) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
mkRowsQueryResponse
|
||||
[ [ ("id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("name", API.mkColumnFieldValue $ Aeson.String "AC/DC")
|
||||
],
|
||||
[ ("id", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
("name", API.mkColumnFieldValue $ Aeson.String "Accept")
|
||||
],
|
||||
[ ("id", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("name", API.mkColumnFieldValue $ Aeson.String "Aerosmith")
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
_mrrResponse
|
||||
`shouldBeYaml` [yaml|
|
||||
data:
|
||||
artists:
|
||||
- id: 1
|
||||
name: AC/DC
|
||||
- id: 2
|
||||
name: Accept
|
||||
- id: 3
|
||||
name: Aerosmith
|
||||
|]
|
||||
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
mkQueryRequest
|
||||
(mkTableName "Artist")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
]
|
||||
& API.qLimit ?~ 3 -- The permissions limit is smaller than the query limit, so it is used
|
||||
)
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "works with an exists-based permissions filter" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -173,12 +219,12 @@ tests _opts = describe "Basic Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 1)
|
||||
mkRowsQueryResponse
|
||||
[ [ ("CustomerId", API.mkColumnFieldValue $ Aeson.Number 1)
|
||||
],
|
||||
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 2)
|
||||
[ ("CustomerId", API.mkColumnFieldValue $ Aeson.Number 2)
|
||||
],
|
||||
[ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 3)
|
||||
[ ("CustomerId", API.mkColumnFieldValue $ Aeson.Number 3)
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
@ -197,31 +243,20 @@ tests _opts = describe "Basic Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Customer" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere =
|
||||
Just $
|
||||
API.Exists (API.UnrelatedTable $ API.TableName ("Employee" :| [])) $
|
||||
API.ApplyBinaryComparisonOperator
|
||||
mkQueryRequest
|
||||
(mkTableName "Customer")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
|
||||
]
|
||||
& API.qWhere
|
||||
?~ API.Exists
|
||||
(API.UnrelatedTable $ mkTableName "Employee")
|
||||
( API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.ColumnName "EmployeeId") $ API.ScalarType "number")
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 1) (API.ScalarType "number")),
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -6,8 +6,8 @@ module Test.DataConnector.MockAgent.CustomScalarsSpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
|
||||
import Harness.Backend.DataConnector.Mock qualified as Mock
|
||||
@ -21,6 +21,7 @@ import Hasura.Backends.DataConnector.API (ColumnName (..), ScalarType (..), Scal
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Backends.DataConnector.API.V0.Expression
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -67,7 +68,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse customScalarsTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse customScalarsTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -86,29 +87,20 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("MyCustomScalarsTable" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
(API.FieldName "MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
(API.FieldName "MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
(API.FieldName "MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
(API.FieldName "MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
(API.FieldName "MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
mkQueryRequest
|
||||
(mkTableName "MyCustomScalarsTable")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "parses scalar literals in where queries" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -133,7 +125,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse customScalarsTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse customScalarsTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -152,56 +144,47 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("MyCustomScalarsTable" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
(API.FieldName "MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
(API.FieldName "MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
(API.FieldName "MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
(API.FieldName "MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
(API.FieldName "MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere =
|
||||
Just $
|
||||
And
|
||||
mkQueryRequest
|
||||
(mkTableName "MyCustomScalarsTable")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
& API.qWhere
|
||||
?~ And
|
||||
[ ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyBooleanColumn"}, _ccColumnType = ScalarType "MyBoolean"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyBooleanColumn") (ScalarType "MyBoolean"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.Bool True) (ScalarType "MyBoolean")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyFloatColumn"}, _ccColumnType = ScalarType "MyFloat"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyFloatColumn") (ScalarType "MyFloat"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.Number 3.14) (ScalarType "MyFloat")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyStringColumn"}, _ccColumnType = ScalarType "MyString"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyStringColumn") (ScalarType "MyString"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.String "foo") (ScalarType "MyString")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyIDColumn"}, _ccColumnType = ScalarType "MyID"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyIDColumn") (ScalarType "MyID"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.String "x") (ScalarType "MyID")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyIntColumn"}, _ccColumnType = ScalarType "MyInt"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyIntColumn") (ScalarType "MyInt"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.Number 42.0) (ScalarType "MyInt")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyAnythingColumn"}, _ccColumnType = ScalarType "MyAnything"})
|
||||
(ComparisonColumn CurrentTable (ColumnName "MyAnythingColumn") (ScalarType "MyAnything"))
|
||||
(ScalarValueComparison $ ScalarValue (Aeson.Object mempty) (ScalarType "MyAnything"))
|
||||
],
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "fails parsing float when expecting int" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -216,7 +199,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -243,7 +226,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -270,7 +253,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -297,7 +280,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -320,7 +303,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -347,7 +330,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -374,7 +357,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -401,7 +384,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -428,7 +411,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -455,7 +438,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -482,7 +465,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -509,7 +492,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -536,7 +519,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -559,7 +542,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -586,7 +569,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -613,7 +596,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -636,7 +619,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -659,7 +642,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -682,7 +665,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -705,7 +688,7 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
}
|
||||
}
|
||||
|]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (rowsResponse myIntTable)
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse (mkRowsQueryResponse myIntTable)
|
||||
|
||||
MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
|
||||
|
||||
@ -717,18 +700,15 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|
||||
|]
|
||||
where
|
||||
customScalarsTable =
|
||||
[ [ (API.FieldName "MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42),
|
||||
(API.FieldName "MyFloatColumn", API.mkColumnFieldValue $ Aeson.Number 3.14),
|
||||
(API.FieldName "MyStringColumn", API.mkColumnFieldValue $ Aeson.String "foo"),
|
||||
(API.FieldName "MyBooleanColumn", API.mkColumnFieldValue $ Aeson.Bool True),
|
||||
(API.FieldName "MyIDColumn", API.mkColumnFieldValue $ Aeson.String "x"),
|
||||
(API.FieldName "MyAnythingColumn", API.mkColumnFieldValue $ Aeson.Object mempty)
|
||||
[ [ ("MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42),
|
||||
("MyFloatColumn", API.mkColumnFieldValue $ Aeson.Number 3.14),
|
||||
("MyStringColumn", API.mkColumnFieldValue $ Aeson.String "foo"),
|
||||
("MyBooleanColumn", API.mkColumnFieldValue $ Aeson.Bool True),
|
||||
("MyIDColumn", API.mkColumnFieldValue $ Aeson.String "x"),
|
||||
("MyAnythingColumn", API.mkColumnFieldValue $ Aeson.Object mempty)
|
||||
]
|
||||
]
|
||||
myIntTable =
|
||||
[ [ (API.FieldName "MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42)
|
||||
[ [ ("MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42)
|
||||
]
|
||||
]
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.DeleteMutationsSpec
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
@ -17,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -107,38 +109,38 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 3,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
|
||||
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
|
||||
( API.FieldName "deletedRows_Artist",
|
||||
[ mkFieldsMap
|
||||
[ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
|
||||
("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
|
||||
( "deletedRows_Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 113),
|
||||
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The X Factor"),
|
||||
( API.FieldName "deletedRows_Artist",
|
||||
mkFieldsMap
|
||||
[ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 113),
|
||||
("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The X Factor"),
|
||||
( "deletedRows_Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 114),
|
||||
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "Virtual XI"),
|
||||
( API.FieldName "deletedRows_Artist",
|
||||
mkFieldsMap
|
||||
[ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 114),
|
||||
("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "Virtual XI"),
|
||||
( "deletedRows_Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -174,27 +176,26 @@ tests _opts = do
|
||||
|]
|
||||
|
||||
let expectedRequest =
|
||||
API.MutationRequest
|
||||
{ API._mrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ API._trSourceTable = API.TableName ("Album" :| []),
|
||||
emptyMutationRequest
|
||||
& API.mrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ API._trSourceTable = mkTableName "Album",
|
||||
API._trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Artist",
|
||||
API.Relationship
|
||||
{ API._rTargetTable = API.TableName ("Artist" :| []),
|
||||
{ API._rTargetTable = mkTableName "Artist",
|
||||
API._rRelationshipType = API.ObjectRelationship,
|
||||
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrInsertSchema = [],
|
||||
API._mrOperations =
|
||||
[ API.DeleteOperation $
|
||||
]
|
||||
& API.mrOperations
|
||||
.~ [ API.DeleteOperation $
|
||||
API.DeleteMutationOperation
|
||||
{ API._dmoTable = API.TableName ("Album" :| []),
|
||||
{ API._dmoTable = mkTableName "Album",
|
||||
API._dmoWhere =
|
||||
Just $
|
||||
API.And
|
||||
@ -208,32 +209,25 @@ tests _opts = do
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 111) (API.ScalarType "number"))
|
||||
],
|
||||
API._dmoReturningFields =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
(API.FieldName "deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( API.FieldName "deletedRows_Artist",
|
||||
mkFieldsMap
|
||||
[ ("deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( "deletedRows_Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Artist")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
|
||||
|
||||
mockAgentGraphqlTest "delete row by pk with delete permissions" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -257,14 +251,14 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 1,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
|
||||
( API.FieldName "Artist",
|
||||
[ mkFieldsMap
|
||||
[ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
|
||||
( "Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -288,27 +282,26 @@ tests _opts = do
|
||||
|]
|
||||
|
||||
let expectedRequest =
|
||||
API.MutationRequest
|
||||
{ API._mrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ API._trSourceTable = API.TableName ("Album" :| []),
|
||||
emptyMutationRequest
|
||||
& API.mrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ API._trSourceTable = mkTableName "Album",
|
||||
API._trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Artist",
|
||||
API.Relationship
|
||||
{ API._rTargetTable = API.TableName ("Artist" :| []),
|
||||
{ API._rTargetTable = mkTableName "Artist",
|
||||
API._rRelationshipType = API.ObjectRelationship,
|
||||
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrInsertSchema = [],
|
||||
API._mrOperations =
|
||||
[ API.DeleteOperation $
|
||||
]
|
||||
& API.mrOperations
|
||||
.~ [ API.DeleteOperation $
|
||||
API.DeleteMutationOperation
|
||||
{ API._dmoTable = API.TableName ("Album" :| []),
|
||||
{ API._dmoTable = mkTableName "Album",
|
||||
API._dmoWhere =
|
||||
Just $
|
||||
API.And
|
||||
@ -322,33 +315,23 @@ tests _opts = do
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 112) (API.ScalarType "number"))
|
||||
],
|
||||
API._dmoReturningFields =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( API.FieldName "Artist",
|
||||
mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( "Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Artist")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -6,8 +6,8 @@ module Test.DataConnector.MockAgent.ErrorSpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest)
|
||||
import Harness.Backend.DataConnector.Mock qualified as Mock
|
||||
@ -19,6 +19,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -92,23 +93,14 @@ tests _opts = describe "Error Protocol Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.InsertMutationsSpec
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
@ -17,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -116,26 +118,26 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 2,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9001),
|
||||
(API.FieldName "insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Super Mega Rock"),
|
||||
( API.FieldName "insertedRows_Artist",
|
||||
[ mkFieldsMap
|
||||
[ ("insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9001),
|
||||
("insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Super Mega Rock"),
|
||||
( "insertedRows_Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Accept")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Accept")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9002),
|
||||
(API.FieldName "insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Accept This"),
|
||||
( API.FieldName "insertedRows_Artist",
|
||||
mkFieldsMap
|
||||
[ ("insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9002),
|
||||
("insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Accept This"),
|
||||
( "insertedRows_Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Accept")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Accept")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -166,49 +168,49 @@ tests _opts = do
|
||||
|]
|
||||
|
||||
let expectedRequest =
|
||||
API.MutationRequest
|
||||
{ API._mrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ API._trSourceTable = API.TableName ("Album" :| []),
|
||||
emptyMutationRequest
|
||||
& API.mrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ API._trSourceTable = mkTableName "Album",
|
||||
API._trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Artist",
|
||||
API.Relationship
|
||||
{ API._rTargetTable = API.TableName ("Artist" :| []),
|
||||
{ API._rTargetTable = mkTableName "Artist",
|
||||
API._rRelationshipType = API.ObjectRelationship,
|
||||
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrInsertSchema =
|
||||
[ API.TableInsertSchema
|
||||
{ API._tisTable = API.TableName ("Album" :| []),
|
||||
]
|
||||
& API.mrInsertSchema
|
||||
.~ [ API.TableInsertSchema
|
||||
{ API._tisTable = mkTableName "Album",
|
||||
API._tisFields =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
(API.FieldName "ArtistId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "ArtistId") (API.ScalarType "number")),
|
||||
(API.FieldName "Title", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("ArtistId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "ArtistId") (API.ScalarType "number")),
|
||||
("Title", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrOperations =
|
||||
[ API.InsertOperation $
|
||||
]
|
||||
& API.mrOperations
|
||||
.~ [ API.InsertOperation $
|
||||
API.InsertMutationOperation
|
||||
{ API._imoTable = API.TableName ("Album" :| []),
|
||||
{ API._imoTable = mkTableName "Album",
|
||||
API._imoRows =
|
||||
[ API.RowObject $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9001),
|
||||
(API.FieldName "ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "Title", API.mkColumnInsertFieldValue $ Aeson.String "Super Mega Rock")
|
||||
mkFieldsMap
|
||||
[ ("AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9001),
|
||||
("ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
|
||||
("Title", API.mkColumnInsertFieldValue $ Aeson.String "Super Mega Rock")
|
||||
],
|
||||
API.RowObject $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9002),
|
||||
(API.FieldName "ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "Title", API.mkColumnInsertFieldValue $ Aeson.String "Accept This")
|
||||
mkFieldsMap
|
||||
[ ("AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9002),
|
||||
("ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
|
||||
("Title", API.mkColumnInsertFieldValue $ Aeson.String "Accept This")
|
||||
]
|
||||
],
|
||||
API._imoPostInsertCheck =
|
||||
@ -218,33 +220,23 @@ tests _opts = do
|
||||
(API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number")
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 2) (API.ScalarType "number")),
|
||||
API._imoReturningFields =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
(API.FieldName "insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( API.FieldName "insertedRows_Artist",
|
||||
mkFieldsMap
|
||||
[ ("insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
( "insertedRows_Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Artist")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -5,6 +5,7 @@ module Test.DataConnector.MockAgent.OrderBySpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
@ -19,6 +20,7 @@ import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Language.GraphQL.Draft.Syntax.QQ qualified as G
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -111,25 +113,17 @@ tests _opts = describe "Order By Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 3,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Just (API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| []))
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
& API.qLimit ?~ 3
|
||||
& API.qOrderBy ?~ API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| [])
|
||||
)
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "can order by aggregates" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -162,37 +156,13 @@ tests _opts = describe "Order By Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Artist" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Artist" :| []),
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Albums",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Album" :| []),
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 2,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy =
|
||||
Just $
|
||||
API.OrderBy
|
||||
mkQueryRequest
|
||||
(mkTableName "Artist")
|
||||
( emptyQuery
|
||||
& API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))]
|
||||
& API.qLimit ?~ 2
|
||||
& API.qOrderBy
|
||||
?~ API.OrderBy
|
||||
( HashMap.fromList
|
||||
[ ( API.RelationshipName "Albums",
|
||||
API.OrderByRelation Nothing mempty
|
||||
@ -212,10 +182,23 @@ tests _opts = describe "Order By Tests" $ do
|
||||
API.Ascending
|
||||
]
|
||||
)
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Artist",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Albums",
|
||||
API.Relationship
|
||||
{ _rTargetTable = mkTableName "Album",
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -5,6 +5,7 @@ module Test.DataConnector.MockAgent.QueryRelationshipsSpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
@ -19,6 +20,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -145,18 +147,18 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"),
|
||||
( API.FieldName "Genre",
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"),
|
||||
( "Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")]
|
||||
mkRowsQueryResponse
|
||||
[ [("Name", API.mkColumnFieldValue $ Aeson.String "Rock")]
|
||||
]
|
||||
),
|
||||
( API.FieldName "MediaType",
|
||||
( "MediaType",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")]
|
||||
mkRowsQueryResponse
|
||||
[ [("Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")]
|
||||
]
|
||||
)
|
||||
]
|
||||
@ -179,23 +181,44 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Track" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Track" :| []),
|
||||
mkQueryRequest
|
||||
(mkTableName "Track")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
( "Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
)
|
||||
),
|
||||
( "MediaType",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "MediaType")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
)
|
||||
)
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Track",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Genre",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Genre" :| []),
|
||||
{ _rTargetTable = mkTableName "Genre",
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
|
||||
}
|
||||
),
|
||||
( API.RelationshipName "MediaType",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("MediaType" :| []),
|
||||
{ _rTargetTable = mkTableName "MediaType",
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping =
|
||||
HashMap.fromList
|
||||
@ -204,58 +227,7 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
( API.FieldName "Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
)
|
||||
),
|
||||
( API.FieldName "MediaType",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "MediaType")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
)
|
||||
)
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "works with an order by that navigates relationships" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -274,19 +246,19 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ ( API.FieldName "Album",
|
||||
mkRowsQueryResponse
|
||||
[ [ ( "Album",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ ( API.FieldName "Artist",
|
||||
mkRowsQueryResponse
|
||||
[ [ ( "Artist",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[[(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Zeca Pagodinho")]]
|
||||
mkRowsQueryResponse
|
||||
[[("Name", API.mkColumnFieldValue $ Aeson.String "Zeca Pagodinho")]]
|
||||
)
|
||||
]
|
||||
]
|
||||
),
|
||||
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Camarão que Dorme e Onda Leva")
|
||||
("Name", API.mkColumnFieldValue $ Aeson.String "Camarão que Dorme e Onda Leva")
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
@ -306,85 +278,34 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Track" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Track" :| []),
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Album",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Album" :| []),
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "AlbumId", API.ColumnName "AlbumId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
},
|
||||
API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Album" :| []),
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Artist",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Artist" :| []),
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
( API.FieldName "Album",
|
||||
mkQueryRequest
|
||||
(mkTableName "Track")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
( "Album",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Album")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ ( API.FieldName "Artist",
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ( "Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Artist")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))])
|
||||
)
|
||||
)
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy =
|
||||
Just $
|
||||
API.OrderBy
|
||||
)
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
& API.qOrderBy
|
||||
?~ API.OrderBy
|
||||
( HashMap.fromList
|
||||
[ ( API.RelationshipName "Album",
|
||||
API.OrderByRelation
|
||||
@ -405,10 +326,36 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
API.OrderByElement [] (API.OrderByColumn (API.ColumnName "Name")) API.Ascending
|
||||
]
|
||||
)
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Track",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Album",
|
||||
API.Relationship
|
||||
{ _rTargetTable = mkTableName "Album",
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "AlbumId", API.ColumnName "AlbumId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
},
|
||||
API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Album",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Artist",
|
||||
API.Relationship
|
||||
{ _rTargetTable = mkTableName "Artist",
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "works with an order by that navigates a relationship with table permissions" $ \_testEnv performGraphqlRequest -> do
|
||||
let headers = [("X-Hasura-Role", testRoleName)]
|
||||
@ -421,8 +368,8 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3)
|
||||
mkRowsQueryResponse
|
||||
[ [ ("EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3)
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
@ -439,56 +386,21 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Employee" :| []),
|
||||
_qrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Customer" :| []),
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "SupportRep",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Employee" :| []),
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "SupportRepId", API.ColumnName "EmployeeId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
},
|
||||
API.TableRelationships
|
||||
{ _trSourceTable = API.TableName ("Employee" :| []),
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "SupportRepForCustomers",
|
||||
API.Relationship
|
||||
{ _rTargetTable = API.TableName ("Customer" :| []),
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "EmployeeId", API.ColumnName "SupportRepId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere =
|
||||
Just $
|
||||
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRepForCustomers") $
|
||||
API.ApplyBinaryComparisonOperator
|
||||
mkQueryRequest
|
||||
(mkTableName "Employee")
|
||||
( emptyQuery
|
||||
& API.qFields ?~ mkFieldsMap [("EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")]
|
||||
& API.qLimit ?~ 1
|
||||
& API.qWhere
|
||||
?~ API.Exists
|
||||
(API.RelatedTable $ API.RelationshipName "SupportRepForCustomers")
|
||||
( API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string")),
|
||||
_qOrderBy =
|
||||
Just $
|
||||
API.OrderBy
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string"))
|
||||
)
|
||||
& API.qOrderBy
|
||||
?~ API.OrderBy
|
||||
( HashMap.fromList
|
||||
[ ( API.RelationshipName "SupportRepForCustomers",
|
||||
API.OrderByRelation
|
||||
@ -504,10 +416,33 @@ tests _opts = describe "Object Relationships Tests" $ do
|
||||
]
|
||||
)
|
||||
(API.OrderByElement [API.RelationshipName "SupportRepForCustomers"] API.OrderByStarCountAggregate API.Descending :| [])
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
)
|
||||
& API.qrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Customer",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "SupportRep",
|
||||
API.Relationship
|
||||
{ _rTargetTable = mkTableName "Employee",
|
||||
_rRelationshipType = API.ObjectRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "SupportRepId", API.ColumnName "EmployeeId")]
|
||||
}
|
||||
)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
]
|
||||
},
|
||||
API.TableRelationships
|
||||
{ _trSourceTable = mkTableName "Employee",
|
||||
_trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "SupportRepForCustomers",
|
||||
API.Relationship
|
||||
{ _rTargetTable = mkTableName "Customer",
|
||||
_rRelationshipType = API.ArrayRelationship,
|
||||
_rColumnMapping = HashMap.fromList [(API.ColumnName "EmployeeId", API.ColumnName "SupportRepId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ module Test.DataConnector.MockAgent.RemoteRelationshipsSpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((.~), _Just)
|
||||
import Control.Lens ((.~), (?~), _Just)
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
@ -24,6 +24,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml (shouldBeYaml, shouldReturnYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (HasCallStack, SpecWith, describe, it, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -211,27 +212,27 @@ tests _opts = do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
mkRowsResponse
|
||||
[ [ ( API.FieldName "query",
|
||||
mkRowsQueryResponse
|
||||
[ [ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkRowsResponse
|
||||
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
],
|
||||
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
[ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
[ ( API.FieldName "query",
|
||||
[ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkRowsResponse
|
||||
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
|
||||
],
|
||||
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
[ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -264,30 +265,20 @@ tests _opts = do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach =
|
||||
Just $
|
||||
NonEmpty.fromList
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
)
|
||||
& API.qrForeach
|
||||
?~ NonEmpty.fromList
|
||||
[ HashMap.fromList [(API.ColumnName "ArtistId", API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))],
|
||||
HashMap.fromList [(API.ColumnName "ArtistId", API.ScalarValue (Aeson.Number 2) (API.ScalarType "number"))]
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "can act as the target of a remote object relationship" $ \testEnv performGraphqlRequest -> do
|
||||
@ -307,30 +298,30 @@ tests _opts = do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
mkRowsResponse
|
||||
[ [ ( API.FieldName "query",
|
||||
mkRowsQueryResponse
|
||||
[ [ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkRowsResponse
|
||||
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
[ ( API.FieldName "query",
|
||||
[ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkRowsResponse
|
||||
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
[ ( API.FieldName "query",
|
||||
[ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkRowsResponse
|
||||
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
("Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -364,31 +355,21 @@ tests _opts = do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach =
|
||||
Just $
|
||||
NonEmpty.fromList
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
)
|
||||
& API.qrForeach
|
||||
?~ NonEmpty.fromList
|
||||
[ HashMap.fromList [(API.ColumnName "AlbumId", API.ScalarValue (Aeson.Number 3) (API.ScalarType "number"))],
|
||||
HashMap.fromList [(API.ColumnName "AlbumId", API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))],
|
||||
HashMap.fromList [(API.ColumnName "AlbumId", API.ScalarValue (Aeson.Number 4) (API.ScalarType "number"))]
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
mockAgentGraphqlTest "can act as the target of an aggregation over a remote array relationship" $ \testEnv performGraphqlRequest -> do
|
||||
@ -413,32 +394,32 @@ tests _opts = do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
mkRowsResponse
|
||||
[ [ ( API.FieldName "query",
|
||||
mkRowsQueryResponse
|
||||
[ [ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkQueryResponse
|
||||
[ [ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
[ [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
],
|
||||
[ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
[ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
|
||||
]
|
||||
]
|
||||
[ (API.FieldName "aggregate_count", Aeson.Number 2)
|
||||
[ ("aggregate_count", Aeson.Number 2)
|
||||
]
|
||||
)
|
||||
],
|
||||
[ ( API.FieldName "query",
|
||||
[ ( "query",
|
||||
API.mkRelationshipFieldValue $
|
||||
mkQueryResponse
|
||||
[ [ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
|
||||
[ [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
|
||||
("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
|
||||
],
|
||||
[ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
[ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
|
||||
]
|
||||
]
|
||||
[ (API.FieldName "aggregate_count", Aeson.Number 2)
|
||||
[ ("aggregate_count", Aeson.Number 2)
|
||||
]
|
||||
)
|
||||
]
|
||||
@ -476,41 +457,23 @@ tests _opts = do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "nodes_AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "nodes_Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[(API.FieldName "aggregate_count", API.StarCount)],
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach =
|
||||
Just $
|
||||
NonEmpty.fromList
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("nodes_AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("nodes_Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
& API.qAggregates ?~ mkFieldsMap [("aggregate_count", API.StarCount)]
|
||||
)
|
||||
& API.qrForeach
|
||||
?~ NonEmpty.fromList
|
||||
[ HashMap.fromList [(API.ColumnName "ArtistId", API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))],
|
||||
HashMap.fromList [(API.ColumnName "ArtistId", API.ScalarValue (Aeson.Number 2) (API.ScalarType "number"))]
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
mkRowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
mkRowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
||||
mkQueryResponse :: [[(API.FieldName, API.FieldValue)]] -> [(API.FieldName, Aeson.Value)] -> API.QueryResponse
|
||||
mkQueryResponse rows aggregates = API.QueryResponse (Just $ HashMap.fromList <$> rows) (Just $ HashMap.fromList aggregates)
|
||||
|
||||
errorTests :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
|
||||
errorTests opts = do
|
||||
it "creating a remote relationship returns an error when it is unsupported by the target" $ \(testEnv, _) -> do
|
||||
|
@ -0,0 +1,40 @@
|
||||
module Test.DataConnector.MockAgent.TestHelpers
|
||||
( mkTableName,
|
||||
mkQueryRequest,
|
||||
emptyQuery,
|
||||
emptyMutationRequest,
|
||||
mkRowsQueryResponse,
|
||||
mkAggregatesQueryResponse,
|
||||
mkQueryResponse,
|
||||
mkFieldsMap,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
|
||||
mkTableName :: Text -> API.TableName
|
||||
mkTableName name = API.TableName (name :| [])
|
||||
|
||||
mkQueryRequest :: API.TableName -> API.Query -> API.QueryRequest
|
||||
mkQueryRequest tableName query = API.QueryRequest tableName [] query Nothing
|
||||
|
||||
emptyQuery :: API.Query
|
||||
emptyQuery = API.Query Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
emptyMutationRequest :: API.MutationRequest
|
||||
emptyMutationRequest = API.MutationRequest [] [] []
|
||||
|
||||
mkRowsQueryResponse :: [[(Text, API.FieldValue)]] -> API.QueryResponse
|
||||
mkRowsQueryResponse rows = API.QueryResponse (Just $ mkFieldsMap <$> rows) Nothing
|
||||
|
||||
mkAggregatesQueryResponse :: [(Text, Aeson.Value)] -> API.QueryResponse
|
||||
mkAggregatesQueryResponse aggregates = API.QueryResponse Nothing (Just $ mkFieldsMap aggregates)
|
||||
|
||||
mkQueryResponse :: [[(Text, API.FieldValue)]] -> [(Text, Aeson.Value)] -> API.QueryResponse
|
||||
mkQueryResponse rows aggregates = API.QueryResponse (Just $ mkFieldsMap <$> rows) (Just $ mkFieldsMap aggregates)
|
||||
|
||||
mkFieldsMap :: [(Text, v)] -> HashMap API.FieldName v
|
||||
mkFieldsMap = HashMap.fromList . fmap (first API.FieldName)
|
@ -5,8 +5,8 @@ module Test.DataConnector.MockAgent.TransformedConfigurationSpec (spec) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
|
||||
import Harness.Backend.DataConnector.Mock qualified as Mock
|
||||
@ -18,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
|
||||
import Harness.Yaml (shouldBeYaml)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec (SpecWith, describe, shouldBe)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -113,9 +114,9 @@ tests _opts = describe "Transformed Configuration Tests" $ do
|
||||
}
|
||||
|]
|
||||
let queryResponse =
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("id", API.mkColumnFieldValue $ Aeson.Number 1),
|
||||
("title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
|
||||
]
|
||||
]
|
||||
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
|
||||
@ -133,25 +134,16 @@ tests _opts = describe "Transformed Configuration Tests" $ do
|
||||
_mrrRecordedRequest
|
||||
`shouldBe` Just
|
||||
( Query $
|
||||
API.QueryRequest
|
||||
{ _qrTable = API.TableName ("Album" :| []),
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery =
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Just 1,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
},
|
||||
_qrForeach = Nothing
|
||||
}
|
||||
mkQueryRequest
|
||||
(mkTableName "Album")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
]
|
||||
& API.qLimit ?~ 1
|
||||
)
|
||||
)
|
||||
|
||||
Aeson.toJSON _mrrRecordedRequestConfig
|
||||
@ -161,6 +153,3 @@ tests _opts = describe "Transformed Configuration Tests" $ do
|
||||
env: "bar env default"
|
||||
session: "foo session default"
|
||||
|]
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.UpdateMutationsSpec
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Lens ((.~), (?~))
|
||||
import Data.Aeson qualified as Aeson
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
@ -18,6 +19,7 @@ import Harness.Yaml
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Prelude
|
||||
import Language.GraphQL.Draft.Syntax.QQ qualified as G
|
||||
import Test.DataConnector.MockAgent.TestHelpers
|
||||
import Test.Hspec
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -127,35 +129,35 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 3,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
[ mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -188,27 +190,26 @@ tests _opts = do
|
||||
|]
|
||||
|
||||
let expectedRequest =
|
||||
API.MutationRequest
|
||||
{ API._mrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ API._trSourceTable = API.TableName ("Track" :| []),
|
||||
emptyMutationRequest
|
||||
& API.mrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ API._trSourceTable = mkTableName "Track",
|
||||
API._trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Genre",
|
||||
API.Relationship
|
||||
{ API._rTargetTable = API.TableName ("Genre" :| []),
|
||||
{ API._rTargetTable = mkTableName "Genre",
|
||||
API._rRelationshipType = API.ObjectRelationship,
|
||||
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrInsertSchema = [],
|
||||
API._mrOperations =
|
||||
[ API.UpdateOperation $
|
||||
]
|
||||
& API.mrOperations
|
||||
.~ [ API.UpdateOperation $
|
||||
API.UpdateMutationOperation
|
||||
{ API._umoTable = API.TableName ("Track" :| []),
|
||||
{ API._umoTable = mkTableName "Track",
|
||||
API._umoUpdates =
|
||||
[ API.SetColumn $
|
||||
API.RowColumnOperatorValue
|
||||
@ -248,31 +249,19 @@ tests _opts = do
|
||||
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number")),
|
||||
API._umoReturningFields =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( "updatedRows_Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
)
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
|
||||
|
||||
mockAgentGraphqlTest "update_many rows with update permissions" $ \_testEnv performGraphqlRequest -> do
|
||||
@ -301,13 +290,13 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 1,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
[ mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -318,24 +307,24 @@ tests _opts = do
|
||||
{ API._morAffectedRows = 2,
|
||||
API._morReturning =
|
||||
Just
|
||||
[ HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
[ mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
],
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
|
||||
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
|
||||
("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
|
||||
( "updatedRows_Genre",
|
||||
API.mkRelationshipFieldValue $
|
||||
rowsResponse
|
||||
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
mkRowsQueryResponse
|
||||
[ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
|
||||
]
|
||||
]
|
||||
)
|
||||
@ -376,50 +365,38 @@ tests _opts = do
|
||||
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
|
||||
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number"))
|
||||
let sharedReturning =
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( API.FieldName "updatedRows_Genre",
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
( "updatedRows_Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
API.Query
|
||||
{ _qFields =
|
||||
Just $
|
||||
HashMap.fromList
|
||||
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
],
|
||||
_qAggregates = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
}
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
)
|
||||
)
|
||||
]
|
||||
let expectedRequest =
|
||||
API.MutationRequest
|
||||
{ API._mrTableRelationships =
|
||||
[ API.TableRelationships
|
||||
{ API._trSourceTable = API.TableName ("Track" :| []),
|
||||
emptyMutationRequest
|
||||
& API.mrTableRelationships
|
||||
.~ [ API.TableRelationships
|
||||
{ API._trSourceTable = mkTableName "Track",
|
||||
API._trRelationships =
|
||||
HashMap.fromList
|
||||
[ ( API.RelationshipName "Genre",
|
||||
API.Relationship
|
||||
{ API._rTargetTable = API.TableName ("Genre" :| []),
|
||||
{ API._rTargetTable = mkTableName "Genre",
|
||||
API._rRelationshipType = API.ObjectRelationship,
|
||||
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
],
|
||||
API._mrInsertSchema = [],
|
||||
API._mrOperations =
|
||||
[ API.UpdateOperation $
|
||||
]
|
||||
& API.mrOperations
|
||||
.~ [ API.UpdateOperation $
|
||||
API.UpdateMutationOperation
|
||||
{ API._umoTable = API.TableName ("Track" :| []),
|
||||
{ API._umoTable = mkTableName "Track",
|
||||
API._umoUpdates =
|
||||
[ API.SetColumn $
|
||||
API.RowColumnOperatorValue
|
||||
@ -457,7 +434,7 @@ tests _opts = do
|
||||
},
|
||||
API.UpdateOperation $
|
||||
API.UpdateMutationOperation
|
||||
{ API._umoTable = API.TableName ("Track" :| []),
|
||||
{ API._umoTable = mkTableName "Track",
|
||||
API._umoUpdates =
|
||||
[ API.SetColumn $
|
||||
API.RowColumnOperatorValue
|
||||
@ -494,8 +471,4 @@ tests _opts = do
|
||||
API._umoReturningFields = sharedReturning
|
||||
}
|
||||
]
|
||||
}
|
||||
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
|
||||
|
||||
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
|
||||
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing
|
||||
|
@ -11,6 +11,7 @@ module Hasura.Backends.DataConnector.API.V0.Query
|
||||
Query (..),
|
||||
qFields,
|
||||
qAggregates,
|
||||
qAggregatesLimit,
|
||||
qLimit,
|
||||
qOffset,
|
||||
qWhere,
|
||||
@ -92,9 +93,14 @@ data Query = Query
|
||||
_qFields :: Maybe (HashMap FieldName Field),
|
||||
-- | Map of aggregate field name to Aggregate definition
|
||||
_qAggregates :: Maybe (HashMap FieldName API.V0.Aggregate),
|
||||
-- | Optionally limit to N results.
|
||||
-- | Optionally limit the maximum number of rows considered while applying
|
||||
-- aggregations. This limit does not apply to returned rows.
|
||||
_qAggregatesLimit :: Maybe Int,
|
||||
-- | Optionally limit the maximum number of returned rows. This limit does not
|
||||
-- apply to records considered while apply aggregations.
|
||||
_qLimit :: Maybe Int,
|
||||
-- | Optionally offset from the Nth result.
|
||||
-- | Optionally offset from the Nth result. This applies to both row
|
||||
-- and aggregation results.
|
||||
_qOffset :: Maybe Int,
|
||||
-- | Optionally constrain the results to satisfy some predicate.
|
||||
_qWhere :: Maybe API.V0.Expression,
|
||||
@ -112,9 +118,11 @@ instance HasCodec Query where
|
||||
.= _qFields
|
||||
<*> optionalFieldOrNull "aggregates" "Aggregate fields of the query"
|
||||
.= _qAggregates
|
||||
<*> optionalFieldOrNull "limit" "Optionally limit to N results"
|
||||
<*> optionalFieldOrNull "aggregates_limit" "Optionally limit the maximum number of rows considered while applying aggregations. This limit does not apply to returned rows."
|
||||
.= _qAggregatesLimit
|
||||
<*> optionalFieldOrNull "limit" "Optionally limit the maximum number of returned rows. This limit does not apply to records considered while apply aggregations."
|
||||
.= _qLimit
|
||||
<*> optionalFieldOrNull "offset" "Optionally offset from the Nth result"
|
||||
<*> optionalFieldOrNull "offset" "Optionally offset from the Nth result. This applies to both row and aggregation results."
|
||||
.= _qOffset
|
||||
<*> optionalFieldOrNull "where" "Optionally constrain the results to satisfy some predicate"
|
||||
.= _qWhere
|
||||
|
@ -598,7 +598,7 @@ findColumnScalarType API.SchemaResponse {..} tableName columnName =
|
||||
columnInfo = find (\API.ColumnInfo {..} -> _ciName == columnName) =<< API._tiColumns <$> tableInfo
|
||||
|
||||
emptyQuery :: API.Query
|
||||
emptyQuery = API.Query Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
emptyQuery = API.Query Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
emptyMutationRequest :: API.MutationRequest
|
||||
emptyMutationRequest = API.MutationRequest mempty mempty mempty
|
||||
|
@ -52,12 +52,24 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
|
||||
it "counts all rows, after applying pagination" $ do
|
||||
let offset = 400
|
||||
let limit = 20
|
||||
let aggregatesLimit = 20
|
||||
let aggregates = Data.mkFieldsMap [("count_all", StarCount)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qLimit ?~ limit >>> qOffset ?~ offset)
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qOffset ?~ offset)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceCount = length . take limit $ drop offset _tdInvoicesRows
|
||||
let invoiceCount = length . take aggregatesLimit $ drop offset _tdInvoicesRows
|
||||
let expectedAggregates = Data.mkFieldsMap [("count_all", Number $ fromIntegral invoiceCount)]
|
||||
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "limit does not limit the count aggregation" $ do
|
||||
let limit = 20
|
||||
let aggregates = Data.mkFieldsMap [("count_all", StarCount)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery . qLimit ?~ limit
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceCount = length _tdInvoicesRows
|
||||
let expectedAggregates = Data.mkFieldsMap [("count_all", Number $ fromIntegral invoiceCount)]
|
||||
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
@ -76,16 +88,16 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "can count all rows with non-null values in a column, after applying pagination and filtering" $ do
|
||||
let limit = 50
|
||||
let aggregatesLimit = 50
|
||||
let where' = ApplyBinaryComparisonOperator GreaterThanOrEqual (_tdCurrentComparisonColumn "InvoiceId" invoiceIdScalarType) (Data.scalarValueComparison (Number 380) invoiceIdScalarType)
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") False)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qLimit ?~ limit >>> qWhere ?~ where')
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qWhere ?~ where')
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceCount =
|
||||
_tdInvoicesRows
|
||||
& filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380))
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString))
|
||||
& length
|
||||
|
||||
@ -106,18 +118,18 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "can count all rows with distinct non-null values in a column, after applying pagination and filtering" $ do
|
||||
let limit = 20
|
||||
let aggregatesLimit = 20
|
||||
let where' = ApplyBinaryComparisonOperator GreaterThanOrEqual (_tdCurrentComparisonColumn "InvoiceId" invoiceIdScalarType) (Data.scalarValueComparison (Number 380) invoiceIdScalarType)
|
||||
-- It is important to add an explicit order by for this query as different database engines will order implicitly resulting in incorrect results
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "InvoiceId" Ascending :| []
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") True)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qLimit ?~ limit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let billingStateCount =
|
||||
_tdInvoicesRows
|
||||
& filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380))
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString))
|
||||
& HashSet.fromList
|
||||
& length
|
||||
@ -127,6 +139,22 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "limit does not limit the column count aggregation" $ do
|
||||
let limit = 50
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") False)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery . qLimit ?~ limit
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceCount =
|
||||
_tdInvoicesRows
|
||||
& mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString))
|
||||
& length
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("count_cols", Number $ fromIntegral invoiceCount)]
|
||||
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
describe "Single Column Function" $ do
|
||||
it "can get the max total from all rows" $ do
|
||||
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)]
|
||||
@ -140,18 +168,18 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "can get the max total from all rows, after applying pagination, filtering and ordering" $ do
|
||||
let limit = 20
|
||||
let aggregatesLimit = 20
|
||||
let where' = ApplyBinaryComparisonOperator Equal (_tdCurrentComparisonColumn "BillingCountry" billingCountryScalarType) (Data.scalarValueComparison (String "USA") billingCountryScalarType)
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingPostalCode" Descending :| [_tdOrderByColumn [] "InvoiceId" Ascending]
|
||||
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qLimit ?~ limit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let maxTotal =
|
||||
_tdInvoicesRows
|
||||
& filter ((^? Data.field "BillingCountry" . Data._ColumnFieldString) >>> (== Just "USA"))
|
||||
& sortOn (Down . (^? Data.field "BillingPostalCode"))
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe ((^? Data.field "Total" . Data._ColumnFieldNumber))
|
||||
& maximum
|
||||
|
||||
@ -190,6 +218,22 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "limit does not limit the single column function aggregation" $ do
|
||||
let limit = 20
|
||||
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery . qLimit ?~ limit
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let maxTotal =
|
||||
_tdInvoicesRows
|
||||
& mapMaybe ((^? Data.field "Total" . Data._ColumnFieldNumber))
|
||||
& maximum
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("max", Number maxTotal)]
|
||||
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
describe "Multiple Aggregates and Returning Rows" $ do
|
||||
it "can get the max total from all rows, the count and the distinct count, simultaneously" $ do
|
||||
let aggregates =
|
||||
@ -246,7 +290,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
let where' = ApplyBinaryComparisonOperator Equal (_tdCurrentComparisonColumn "BillingCountry" billingCountryScalarType) (Data.scalarValueComparison (String "Canada") billingCountryScalarType)
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingAddress" Ascending :| [_tdOrderByColumn [] "InvoiceId" Ascending]
|
||||
let aggregates = Data.mkFieldsMap [("min", singleColumnAggregateMin (_tdColumnName "Total") invoiceTotalScalarType)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qFields ?~ fields >>> qLimit ?~ limit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qFields ?~ fields >>> qLimit ?~ limit >>> qAggregatesLimit ?~ limit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceRows =
|
||||
@ -266,6 +310,56 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseRows response `rowsShouldBe` expectedRows
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
|
||||
it "limit limits the number of returned rows but not the rows considered by the aggregate function" $ do
|
||||
let limit = 20
|
||||
let fields = Data.mkFieldsMap [("InvoiceId", _tdColumnField _tdInvoicesTableName "InvoiceId")]
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "InvoiceId" Ascending :| []
|
||||
let aggregates = Data.mkFieldsMap [("count_all", StarCount)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qFields ?~ fields >>> qLimit ?~ limit >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let invoiceCount = length _tdInvoicesRows
|
||||
let expectedAggregates = Data.mkFieldsMap [("count_all", Number $ fromIntegral invoiceCount)]
|
||||
let expectedRows = take limit $ Data.filterColumnsByQueryFields (_qrQuery queryRequest) <$> _tdInvoicesRows
|
||||
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
Data.responseRows response `rowsShouldBe` expectedRows
|
||||
|
||||
it "aggregates limit is applied separately to row limit" $ do
|
||||
let aggregatesLimit = 30
|
||||
let limit = 20
|
||||
let fields =
|
||||
Data.mkFieldsMap
|
||||
[ ("InvoiceId", _tdColumnField _tdInvoicesTableName "InvoiceId"),
|
||||
("BillingCountry", _tdColumnField _tdInvoicesTableName "BillingCountry")
|
||||
]
|
||||
let where' = ApplyBinaryComparisonOperator Equal (_tdCurrentComparisonColumn "BillingCountry" billingCountryScalarType) (Data.scalarValueComparison (String "Canada") billingCountryScalarType)
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingAddress" Ascending :| [_tdOrderByColumn [] "InvoiceId" Ascending]
|
||||
let aggregates = Data.mkFieldsMap [("min", singleColumnAggregateMin (_tdColumnName "Total") invoiceTotalScalarType)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qFields ?~ fields >>> qAggregatesLimit ?~ aggregatesLimit >>> qLimit ?~ limit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let aggregateLimitedInvoiceRows =
|
||||
_tdInvoicesRows
|
||||
& filter ((^? Data.field "BillingCountry" . Data._ColumnFieldString) >>> (== Just "Canada"))
|
||||
& sortOn (^? Data.field "BillingAddress")
|
||||
& take aggregatesLimit
|
||||
|
||||
-- Limit is smaller than aggregatesLimit, so we can just take from the aggregateLimitedInvoiceRows
|
||||
let invoiceRows = take limit aggregateLimitedInvoiceRows
|
||||
|
||||
let maxTotal =
|
||||
aggregateLimitedInvoiceRows
|
||||
& take limit
|
||||
& mapMaybe ((^? Data.field "Total" . Data._ColumnFieldNumber))
|
||||
& aggregate (Number . minimum)
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("min", maxTotal)]
|
||||
let expectedRows = Data.filterColumnsByQueryFields (_qrQuery queryRequest) <$> invoiceRows
|
||||
|
||||
Data.responseRows response `rowsShouldBe` expectedRows
|
||||
Data.responseAggregates response `jsonShouldBe` expectedAggregates
|
||||
|
||||
when (isJust relationshipCapabilities) $
|
||||
describe "Aggregates via Relationships" $ do
|
||||
it "can query aggregates via an array relationship" $ do
|
||||
@ -387,8 +481,8 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
describe "Aggregates over ordered and paginated tables" $ do
|
||||
it "orders by a column" $ do
|
||||
let offset = 2
|
||||
let limit = 5
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Descending :| []
|
||||
let aggregatesLimit = 5
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Ascending :| []
|
||||
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Title") albumTitleScalarType)]
|
||||
let queryRequest =
|
||||
albumsQueryRequest
|
||||
@ -396,15 +490,15 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
%~ ( qAggregates ?~ aggregates
|
||||
>>> qOrderBy ?~ orderBy
|
||||
>>> qOffset ?~ offset
|
||||
>>> qLimit ?~ limit
|
||||
>>> qAggregatesLimit ?~ aggregatesLimit
|
||||
)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
let names =
|
||||
_tdAlbumsRows
|
||||
& sortOn (Down . (^? Data.field "Title"))
|
||||
& sortOn ((^? Data.field "Title"))
|
||||
& drop offset
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)]
|
||||
@ -415,7 +509,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
when (isJust relationshipCapabilities) . describe "involving related tables in the ordering" $ do
|
||||
it "orders by a column" $ do
|
||||
let offset = 10
|
||||
let limit = 50
|
||||
let aggregatesLimit = 50
|
||||
let orderByRelations = HashMap.fromList [(_tdArtistRelationshipName, OrderByRelation Nothing mempty)]
|
||||
let orderBy =
|
||||
OrderBy orderByRelations $
|
||||
@ -431,7 +525,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
%~ ( qAggregates ?~ aggregates
|
||||
>>> qOrderBy ?~ orderBy
|
||||
>>> qOffset ?~ offset
|
||||
>>> qLimit ?~ limit
|
||||
>>> qAggregatesLimit ?~ aggregatesLimit
|
||||
)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -442,7 +536,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
_tdAlbumsRows
|
||||
& sortOn (\album -> getRelatedArtist album ^? _Just . Data.field "Name")
|
||||
& drop offset
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)]
|
||||
@ -452,7 +546,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
|
||||
it "orders by an aggregate" $ do
|
||||
let offset = 15
|
||||
let limit = 10
|
||||
let aggregatesLimit = 10
|
||||
let orderByRelations = HashMap.fromList [(_tdTracksRelationshipName, OrderByRelation Nothing mempty)]
|
||||
let orderBy =
|
||||
OrderBy orderByRelations $
|
||||
@ -468,7 +562,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
%~ ( qAggregates ?~ aggregates
|
||||
>>> qOrderBy ?~ orderBy
|
||||
>>> qOffset ?~ offset
|
||||
>>> qLimit ?~ limit
|
||||
>>> qAggregatesLimit ?~ aggregatesLimit
|
||||
)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -480,7 +574,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
_tdAlbumsRows
|
||||
& sortOn (\album -> (Down $ getRelatedTracksCount album, Down $ album ^? Data.field "Title" . Data._ColumnFieldString))
|
||||
& drop offset
|
||||
& take limit
|
||||
& take aggregatesLimit
|
||||
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
|
||||
|
||||
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)]
|
||||
@ -549,7 +643,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
tracksAggregates = Data.mkFieldsMap [("aggregate_count", StarCount)]
|
||||
tracksWhere = ApplyBinaryComparisonOperator LessThan (_tdCurrentComparisonColumn "Milliseconds" millisecondsScalarType) (Data.scalarValueComparison (Number 300000) millisecondsScalarType)
|
||||
tracksOrderBy = OrderBy mempty $ _tdOrderByColumn [] "Name" Descending :| []
|
||||
tracksSubquery = Query (Just tracksFields) (Just tracksAggregates) Nothing Nothing (Just tracksWhere) (Just tracksOrderBy)
|
||||
tracksSubquery = Query (Just tracksFields) (Just tracksAggregates) Nothing Nothing Nothing (Just tracksWhere) (Just tracksOrderBy)
|
||||
albumsFields =
|
||||
Data.mkFieldsMap
|
||||
[ ("nodes_Title", _tdColumnField _tdAlbumsTableName "Title"),
|
||||
@ -568,7 +662,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
ApplyBinaryComparisonOperator LessThan (_tdCurrentComparisonColumn "Name" artistNameScalarType) (Data.scalarValueComparison (String "B") artistNameScalarType)
|
||||
]
|
||||
artistOrderBy = OrderBy mempty $ _tdOrderByColumn [] "Name" Descending :| []
|
||||
artistQuery = Query (Just artistFields) Nothing (Just 3) (Just 1) (Just artistWhere) (Just artistOrderBy)
|
||||
artistQuery = Query (Just artistFields) Nothing Nothing (Just 3) (Just 1) (Just artistWhere) (Just artistOrderBy)
|
||||
in QueryRequest
|
||||
_tdArtistsTableName
|
||||
[ Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships,
|
||||
|
@ -146,6 +146,7 @@ translateAnnSelect sessionVariables translateFieldsAndAggregates tableName selec
|
||||
API.Query
|
||||
{ _qFields = mapFieldNameHashMap <$> _faaFields,
|
||||
_qAggregates = mapFieldNameHashMap <$> _faaAggregates,
|
||||
_qAggregatesLimit = _saLimit (_asnArgs selectG) <* _faaAggregates, -- Only include the aggregates limit if we actually have aggregrates
|
||||
_qLimit =
|
||||
fmap getMin $
|
||||
foldMap
|
||||
@ -310,6 +311,7 @@ translateAnnField sessionVariables sourceTableName = \case
|
||||
{ _qFields = Just $ mapFieldNameHashMap fields,
|
||||
_qAggregates = mempty,
|
||||
_qWhere = whereClause,
|
||||
_qAggregatesLimit = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qOrderBy = Nothing
|
||||
|
@ -89,6 +89,7 @@ mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema ar
|
||||
API.Query
|
||||
{ _qFields = Just $ mapFieldNameHashMap fields,
|
||||
_qAggregates = Nothing,
|
||||
_qAggregatesLimit = Nothing,
|
||||
_qLimit = Nothing,
|
||||
_qOffset = Nothing,
|
||||
_qWhere = whereClause,
|
||||
|
@ -41,7 +41,7 @@ spec = do
|
||||
}
|
||||
|]
|
||||
describe "RelationshipField" $ do
|
||||
let query = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing
|
||||
let query = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
testToFromJSONToSchema
|
||||
(RelField $ RelationshipField (RelationshipName "a_relationship") query)
|
||||
[aesonQQ|
|
||||
@ -57,6 +57,7 @@ spec = do
|
||||
Query
|
||||
{ _qFields = Just $ HashMap.fromList [(FieldName "my_field_alias", ColumnField (ColumnName "my_field_name") (ScalarType "string"))],
|
||||
_qAggregates = Just $ HashMap.fromList [(FieldName "my_aggregate", StarCount)],
|
||||
_qAggregatesLimit = Just 5,
|
||||
_qLimit = Just 10,
|
||||
_qOffset = Just 20,
|
||||
_qWhere = Just $ And [],
|
||||
@ -67,6 +68,7 @@ spec = do
|
||||
[aesonQQ|
|
||||
{ "fields": {"my_field_alias": {"type": "column", "column": "my_field_name", "column_type": "string"}},
|
||||
"aggregates": { "my_aggregate": { "type": "star_count" } },
|
||||
"aggregates_limit": 5,
|
||||
"limit": 10,
|
||||
"offset": 20,
|
||||
"where": {"type": "and", "expressions": []},
|
||||
@ -91,7 +93,7 @@ spec = do
|
||||
QueryRequest
|
||||
{ _qrTable = TableName ["my_table"],
|
||||
_qrTableRelationships = [],
|
||||
_qrQuery = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing,
|
||||
_qrQuery = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing Nothing,
|
||||
_qrForeach = Just (HashMap.fromList [(ColumnName "my_id", ScalarValue (J.Number 666) (ScalarType "number"))] :| [])
|
||||
}
|
||||
testToFromJSONToSchema
|
||||
@ -181,6 +183,7 @@ genQuery =
|
||||
<*> Gen.maybe (genFieldMap genAggregate)
|
||||
<*> Gen.maybe (Gen.int defaultRange)
|
||||
<*> Gen.maybe (Gen.int defaultRange)
|
||||
<*> Gen.maybe (Gen.int defaultRange)
|
||||
<*> Gen.maybe genExpression
|
||||
<*> Gen.maybe genOrderBy
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user