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:
Daniel Chambers 2023-03-20 15:00:34 +11:00 committed by hasura-bot
parent 6e1ebd6a4b
commit e9c697aaa9
30 changed files with 1415 additions and 1341 deletions

View File

@ -473,7 +473,125 @@ The rows returned by the query must be put into the `rows` property array in the
#### Pagination #### 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 #### Filters

View File

@ -1,6 +1,6 @@
{ {
"name": "@hasura/dc-api-types", "name": "@hasura/dc-api-types",
"version": "0.26.0", "version": "0.27.0",
"description": "Hasura GraphQL Engine Data Connector Agent API types", "description": "Hasura GraphQL Engine Data Connector Agent API types",
"author": "Hasura (https://github.com/hasura/graphql-engine)", "author": "Hasura (https://github.com/hasura/graphql-engine)",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -1305,6 +1305,13 @@
"nullable": true, "nullable": true,
"type": "object" "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": { "fields": {
"additionalProperties": { "additionalProperties": {
"$ref": "#/components/schemas/Field" "$ref": "#/components/schemas/Field"
@ -1314,14 +1321,14 @@
"type": "object" "type": "object"
}, },
"limit": { "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, "maximum": 9223372036854776000,
"minimum": -9223372036854776000, "minimum": -9223372036854776000,
"nullable": true, "nullable": true,
"type": "number" "type": "number"
}, },
"offset": { "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, "maximum": 9223372036854776000,
"minimum": -9223372036854776000, "minimum": -9223372036854776000,
"nullable": true, "nullable": true,

View File

@ -12,16 +12,20 @@ export type Query = {
* Aggregate fields of the query * Aggregate fields of the query
*/ */
aggregates?: Record<string, Aggregate> | null; 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 of the query
*/ */
fields?: Record<string, Field> | null; 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; 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; offset?: number | null;
order_by?: OrderBy; order_by?: OrderBy;

View File

@ -24,7 +24,7 @@
}, },
"dc-api-types": { "dc-api-types": {
"name": "@hasura/dc-api-types", "name": "@hasura/dc-api-types",
"version": "0.26.0", "version": "0.27.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",
@ -2227,7 +2227,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"mathjs": "^11.0.0", "mathjs": "^11.0.0",
"pino-pretty": "^8.0.0", "pino-pretty": "^8.0.0",
@ -2547,7 +2547,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"fastify-metrics": "^9.2.1", "fastify-metrics": "^9.2.1",
"nanoid": "^3.3.4", "nanoid": "^3.3.4",
@ -2868,7 +2868,7 @@
"version": "file:reference", "version": "file:reference",
"requires": { "requires": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",
"@types/node": "^16.11.49", "@types/node": "^16.11.49",
"@types/xml2js": "^0.4.11", "@types/xml2js": "^0.4.11",
@ -3080,7 +3080,7 @@
"version": "file:sqlite", "version": "file:sqlite",
"requires": { "requires": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",
"@types/node": "^16.11.49", "@types/node": "^16.11.49",
"@types/sqlite3": "^3.1.8", "@types/sqlite3": "^3.1.8",

View File

@ -10,7 +10,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"mathjs": "^11.0.0", "mathjs": "^11.0.0",
"pino-pretty": "^8.0.0", "pino-pretty": "^8.0.0",
@ -52,7 +52,7 @@
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
}, },
"node_modules/@hasura/dc-api-types": { "node_modules/@hasura/dc-api-types": {
"version": "0.26.0", "version": "0.27.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",

View File

@ -22,7 +22,7 @@
}, },
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"mathjs": "^11.0.0", "mathjs": "^11.0.0",
"pino-pretty": "^8.0.0", "pino-pretty": "^8.0.0",

View File

@ -578,12 +578,22 @@ export const queryData = (getTable: (tableName: TableName) => Record<string, Raw
const filteredRows = filterIterable(rows, makeFilterPredicate(query.where ?? null, getComparisonColumnValue, performExistsSubquery)); 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 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 const projectedRows = query.fields
? paginatedRows.map(projectRow(query.fields, findRelationship, performNewQuery)) ? paginatedRows.map(projectRow(query.fields, findRelationship, performNewQuery))
: null; : null;
const calculatedAggregates = query.aggregates const calculatedAggregates = query.aggregates
? calculateAggregates(paginatedRows, query.aggregates) ? calculateAggregates(paginatedRowsForAggregation, query.aggregates)
: null; : null;
return { return {
aggregates: calculatedAggregates, aggregates: calculatedAggregates,

View File

@ -10,7 +10,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.26.0", "@hasura/dc-api-types": "0.27.0",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"fastify-metrics": "^9.2.1", "fastify-metrics": "^9.2.1",
"nanoid": "^3.3.4", "nanoid": "^3.3.4",
@ -57,7 +57,7 @@
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
}, },
"node_modules/@hasura/dc-api-types": { "node_modules/@hasura/dc-api-types": {
"version": "0.26.0", "version": "0.27.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@tsconfig/node16": "^1.0.3", "@tsconfig/node16": "^1.0.3",

View File

@ -22,7 +22,7 @@
}, },
"dependencies": { "dependencies": {
"@fastify/cors": "^8.1.0", "@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-metrics": "^9.2.1",
"fastify": "^4.13.0", "fastify": "^4.13.0",
"nanoid": "^3.3.4", "nanoid": "^3.3.4",

View File

@ -23,9 +23,6 @@ import {
OrderByRelation, OrderByRelation,
OrderByElement, OrderByElement,
OrderByTarget, OrderByTarget,
Query,
ColumnFieldValue,
NullColumnFieldValue,
ScalarValue, ScalarValue,
} from "@hasura/dc-api-types"; } from "@hasura/dc-api-types";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
@ -335,12 +332,13 @@ function table_query(
fields: Fields | null, fields: Fields | null,
aggregates: Aggregates | null, aggregates: Aggregates | null,
wWhere: Expression | null, wWhere: Expression | null,
aggregatesLimit: number | null,
wLimit: number | null, wLimit: number | null,
wOffset: number | null, wOffset: number | null,
wOrder: OrderBy | null, wOrder: OrderBy | null,
): string { ): string {
const tableAlias = generateTableAlias(tableName); 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 // 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 // SQLite. See https://sqlite.org/forum/forumpost/e3b101fb3234272b for more details. This approach still works fine
// for older versions too. // 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 // 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 // "object" relationship that accidentally actually is an array relationship
const limit = const [limit, aggregatesLimit] =
r.relationship_type === "object" r.relationship_type === "object"
? 1 ? [1, 1]
: coerceUndefinedToNull(field.query.limit); : [coerceUndefinedToNull(field.query.limit), coerceUndefinedToNull(field.query.aggregates_limit)];
return tag("relationship", table_query( return tag("relationship", table_query(
ts, ts,
@ -386,6 +384,7 @@ function relationship(ts: TableRelationships[], r: Relationship, field: Relation
coerceUndefinedToNull(field.query.fields), coerceUndefinedToNull(field.query.fields),
coerceUndefinedToNull(field.query.aggregates), coerceUndefinedToNull(field.query.aggregates),
coerceUndefinedToNull(field.query.where), coerceUndefinedToNull(field.query.where),
aggregatesLimit,
limit, limit,
coerceUndefinedToNull(field.query.offset), coerceUndefinedToNull(field.query.offset),
coerceUndefinedToNull(field.query.order_by), coerceUndefinedToNull(field.query.order_by),
@ -657,6 +656,7 @@ function query(request: QueryRequest): string {
coerceUndefinedToNull(request.query.fields), coerceUndefinedToNull(request.query.fields),
coerceUndefinedToNull(request.query.aggregates), coerceUndefinedToNull(request.query.aggregates),
coerceUndefinedToNull(request.query.where), coerceUndefinedToNull(request.query.where),
coerceUndefinedToNull(request.query.aggregates_limit),
coerceUndefinedToNull(request.query.limit), coerceUndefinedToNull(request.query.limit),
coerceUndefinedToNull(request.query.offset), coerceUndefinedToNull(request.query.offset),
coerceUndefinedToNull(request.query.order_by), coerceUndefinedToNull(request.query.order_by),
@ -740,6 +740,7 @@ function foreach_query(foreachIds: Record<string, ScalarValue>[], request: Query
null, null,
null, null,
null, null,
null,
); );
return tag('foreach_query', `WITH ${escapeTableName(foreachTableName)} AS (${foreachIdsTableValue}) SELECT ${tableSubquery} AS data`); return tag('foreach_query', `WITH ${escapeTableName(foreachTableName)} AS (${foreachIdsTableValue}) SELECT ${tableSubquery} AS data`);
} }

View File

@ -119,6 +119,7 @@ library
Test.DataConnector.MockAgent.OrderBySpec Test.DataConnector.MockAgent.OrderBySpec
Test.DataConnector.MockAgent.QueryRelationshipsSpec Test.DataConnector.MockAgent.QueryRelationshipsSpec
Test.DataConnector.MockAgent.RemoteRelationshipsSpec Test.DataConnector.MockAgent.RemoteRelationshipsSpec
Test.DataConnector.MockAgent.TestHelpers
Test.DataConnector.MockAgent.TransformedConfigurationSpec Test.DataConnector.MockAgent.TransformedConfigurationSpec
Test.DataConnector.MockAgent.UpdateMutationsSpec Test.DataConnector.MockAgent.UpdateMutationsSpec
Test.DataConnector.QuerySpec Test.DataConnector.QuerySpec

View File

@ -4,6 +4,7 @@ module Test.DataConnector.MockAgent.AggregateQuerySpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE 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.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Language.GraphQL.Draft.Syntax.QQ qualified as G import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -108,14 +110,14 @@ tests _opts = describe "Aggregate Query Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("ArtistIds_Id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"), ("ArtistNames_Name", API.mkColumnFieldValue $ Aeson.String "AC/DC"),
( API.FieldName "nodes_Albums", ( "nodes_Albums",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")], [ [("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")] [("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")]
] ]
) )
] ]
@ -142,57 +144,40 @@ tests _opts = describe "Aggregate Query Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Artist" :| []), (mkTableName "Artist")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields
{ _trSourceTable = API.TableName ("Artist" :| []), ?~ mkFieldsMap
_trRelationships = [ ("ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
HashMap.fromList ("ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
[ ( API.RelationshipName "Albums", ( "nodes_Albums",
API.Relationship API.RelField
{ _rTargetTable = API.TableName ("Album" :| []), ( API.RelationshipField
_rRelationshipType = API.ArrayRelationship, (API.RelationshipName "Albums")
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] ( emptyQuery
} & API.qFields ?~ mkFieldsMap [("nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))]
) )
] )
} )
], ]
_qrQuery = & API.qLimit ?~ 1
API.Query )
{ _qFields = & API.qrTableRelationships
Just $ .~ [ API.TableRelationships
HashMap.fromList { _trSourceTable = mkTableName "Artist",
[ (API.FieldName "ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")), _trRelationships =
(API.FieldName "ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")), HashMap.fromList
( API.FieldName "nodes_Albums", [ ( API.RelationshipName "Albums",
API.RelField API.Relationship
( API.RelationshipField { _rTargetTable = mkTableName "Album",
(API.RelationshipName "Albums") _rRelationshipType = API.ArrayRelationship,
API.Query _rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
{ _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 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 = let aggregates =
[ (API.FieldName "counts_count", Aeson.Number 2), [ ("counts_count", Aeson.Number 2),
(API.FieldName "counts_uniqueBillingCountries", Aeson.Number 2), ("counts_uniqueBillingCountries", Aeson.Number 2),
(API.FieldName "ids_minimum_Id", Aeson.Number 1), ("ids_minimum_Id", Aeson.Number 1),
(API.FieldName "ids_max_InvoiceId", Aeson.Number 2) ("ids_max_InvoiceId", Aeson.Number 2)
] ]
rows = rows =
[ [ ( API.FieldName "nodes_Lines", [ [ ( "nodes_Lines",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
aggregatesResponse mkAggregatesQueryResponse
[ (API.FieldName "aggregate_count", Aeson.Number 2) [ ("aggregate_count", Aeson.Number 2)
] ]
) )
], ],
[ ( API.FieldName "nodes_Lines", [ ( "nodes_Lines",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
aggregatesResponse mkAggregatesQueryResponse
[ (API.FieldName "aggregate_count", Aeson.Number 4) [ ("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 MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
@ -273,72 +258,47 @@ tests _opts = describe "Aggregate Query Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Invoice" :| []), (mkTableName "Invoice")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields
{ _trSourceTable = API.TableName ("Invoice" :| []), ?~ mkFieldsMap
_trRelationships = [ ( "nodes_Lines",
HashMap.fromList API.RelField
[ ( API.RelationshipName "InvoiceLines", ( API.RelationshipField
API.Relationship (API.RelationshipName "InvoiceLines")
{ _rTargetTable = API.TableName ("InvoiceLine" :| []), ( emptyQuery & API.qAggregates ?~ mkFieldsMap [("aggregate_count", API.StarCount)]
_rRelationshipType = API.ArrayRelationship, )
_rColumnMapping = HashMap.fromList [(API.ColumnName "InvoiceId", API.ColumnName "InvoiceId")] )
} )
) ]
] & API.qAggregates
} ?~ mkFieldsMap
], [ ("counts_count", API.StarCount),
_qrQuery = ("counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
API.Query ("ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId") (API.ScalarType "number"))),
{ _qFields = ("ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId") (API.ScalarType "number")))
Just $ ]
HashMap.fromList & API.qLimit ?~ 2
[ ( API.FieldName "nodes_Lines", & API.qAggregatesLimit ?~ 2
API.RelField )
( API.RelationshipField & API.qrTableRelationships
(API.RelationshipName "InvoiceLines") .~ [ API.TableRelationships
API.Query { _trSourceTable = mkTableName "Invoice",
{ _qFields = Nothing, _trRelationships =
_qAggregates = HashMap.fromList
Just $ [ ( API.RelationshipName "InvoiceLines",
HashMap.fromList API.Relationship
[(API.FieldName "aggregate_count", API.StarCount)], { _rTargetTable = mkTableName "InvoiceLine",
_qLimit = Nothing, _rRelationshipType = API.ArrayRelationship,
_qOffset = Nothing, _rColumnMapping = HashMap.fromList [(API.ColumnName "InvoiceId", API.ColumnName "InvoiceId")]
_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.ColumnName -> API.ScalarType -> API.SingleColumnAggregate
singleColumnAggregateMax = API.SingleColumnAggregate $ API.SingleColumnAggregateFunction [G.name|max|] singleColumnAggregateMax = API.SingleColumnAggregate $ API.SingleColumnAggregateFunction [G.name|max|]

View File

@ -6,9 +6,9 @@ module Test.DataConnector.MockAgent.BasicQuerySpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse) import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock import Harness.Backend.DataConnector.Mock qualified as Mock
@ -20,6 +20,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml) import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -73,19 +74,19 @@ sourceMetadata =
configuration: configuration:
custom_root_fields: custom_root_fields:
select: artists select: artists
select_by_pk: artists_by_pk
column_config: column_config:
ArtistId: ArtistId:
custom_name: id custom_name: id
Name: Name:
custom_name: name custom_name: name
array_relationships: select_permissions:
- name: albums - role: *testRoleName
using: permission:
manual_configuration: columns:
remote_table: [Album] - ArtistId
column_mapping: - Name
ArtistId: ArtistId limit: 3
filter: {}
- table: [Employee] - table: [Employee]
- table: [Customer] - table: [Customer]
select_permissions: select_permissions:
@ -118,9 +119,9 @@ tests _opts = describe "Basic Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You") ("title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
] ]
] ]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
@ -138,25 +139,70 @@ tests _opts = describe "Basic Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
Just $ ("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
HashMap.fromList ]
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")), & API.qLimit ?~ 1
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")) )
], )
_qAggregates = Nothing,
_qLimit = Just 1, mockAgentGraphqlTest "permissions-based row limits are applied" $ \_testEnv performGraphqlRequest -> do
_qOffset = Nothing, let headers = [("X-Hasura-Role", testRoleName)]
_qWhere = Nothing, let graphqlRequest =
_qOrderBy = Nothing [graphql|
}, query getArtists {
_qrForeach = Nothing 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 mockAgentGraphqlTest "works with an exists-based permissions filter" $ \_testEnv performGraphqlRequest -> do
@ -173,12 +219,12 @@ tests _opts = describe "Basic Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "CustomerId", API.mkColumnFieldValue $ Aeson.Number 1) [ [ ("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 let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
@ -197,31 +243,20 @@ tests _opts = describe "Basic Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Customer" :| []), (mkTableName "Customer")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
Just $ ]
HashMap.fromList & API.qWhere
[ (API.FieldName "CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number") ?~ API.Exists
], (API.UnrelatedTable $ mkTableName "Employee")
_qAggregates = Nothing, ( API.ApplyBinaryComparisonOperator
_qLimit = Nothing, API.Equal
_qOffset = Nothing, (API.ComparisonColumn API.CurrentTable (API.ColumnName "EmployeeId") $ API.ScalarType "number")
_qWhere = (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))
Just $ )
API.Exists (API.UnrelatedTable $ API.TableName ("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
}
) )
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -6,8 +6,8 @@ module Test.DataConnector.MockAgent.CustomScalarsSpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse) import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock 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 qualified as API
import Hasura.Backends.DataConnector.API.V0.Expression import Hasura.Backends.DataConnector.API.V0.Expression
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) 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 MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
@ -86,29 +87,20 @@ tests _opts = describe "Custom scalar parsing tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("MyCustomScalarsTable" :| []), (mkTableName "MyCustomScalarsTable")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
Just $ ("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
HashMap.fromList ("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
[ (API.FieldName "MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"), ("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
(API.FieldName "MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"), ("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
(API.FieldName "MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"), ("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
(API.FieldName "MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"), ]
(API.FieldName "MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"), & API.qLimit ?~ 1
(API.FieldName "MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything") )
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
},
_qrForeach = Nothing
}
) )
mockAgentGraphqlTest "parses scalar literals in where queries" $ \_testEnv performGraphqlRequest -> do 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 MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
@ -152,56 +144,47 @@ tests _opts = describe "Custom scalar parsing tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("MyCustomScalarsTable" :| []), (mkTableName "MyCustomScalarsTable")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
Just $ ("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
HashMap.fromList ("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
[ (API.FieldName "MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"), ("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
(API.FieldName "MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"), ("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
(API.FieldName "MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"), ("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
(API.FieldName "MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"), ]
(API.FieldName "MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"), & API.qLimit ?~ 1
(API.FieldName "MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything") & API.qWhere
], ?~ And
_qAggregates = Nothing, [ ApplyBinaryComparisonOperator
_qLimit = Just 1, Equal
_qOffset = Nothing, (ComparisonColumn CurrentTable (ColumnName "MyBooleanColumn") (ScalarType "MyBoolean"))
_qWhere = (ScalarValueComparison $ ScalarValue (Aeson.Bool True) (ScalarType "MyBoolean")),
Just $ ApplyBinaryComparisonOperator
And Equal
[ ApplyBinaryComparisonOperator (ComparisonColumn CurrentTable (ColumnName "MyFloatColumn") (ScalarType "MyFloat"))
Equal (ScalarValueComparison $ ScalarValue (Aeson.Number 3.14) (ScalarType "MyFloat")),
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyBooleanColumn"}, _ccColumnType = ScalarType "MyBoolean"}) ApplyBinaryComparisonOperator
(ScalarValueComparison $ ScalarValue (Aeson.Bool True) (ScalarType "MyBoolean")), Equal
ApplyBinaryComparisonOperator (ComparisonColumn CurrentTable (ColumnName "MyStringColumn") (ScalarType "MyString"))
Equal (ScalarValueComparison $ ScalarValue (Aeson.String "foo") (ScalarType "MyString")),
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyFloatColumn"}, _ccColumnType = ScalarType "MyFloat"}) ApplyBinaryComparisonOperator
(ScalarValueComparison $ ScalarValue (Aeson.Number 3.14) (ScalarType "MyFloat")), Equal
ApplyBinaryComparisonOperator (ComparisonColumn CurrentTable (ColumnName "MyIDColumn") (ScalarType "MyID"))
Equal (ScalarValueComparison $ ScalarValue (Aeson.String "x") (ScalarType "MyID")),
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyStringColumn"}, _ccColumnType = ScalarType "MyString"}) ApplyBinaryComparisonOperator
(ScalarValueComparison $ ScalarValue (Aeson.String "foo") (ScalarType "MyString")), Equal
ApplyBinaryComparisonOperator (ComparisonColumn CurrentTable (ColumnName "MyIntColumn") (ScalarType "MyInt"))
Equal (ScalarValueComparison $ ScalarValue (Aeson.Number 42.0) (ScalarType "MyInt")),
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyIDColumn"}, _ccColumnType = ScalarType "MyID"}) ApplyBinaryComparisonOperator
(ScalarValueComparison $ ScalarValue (Aeson.String "x") (ScalarType "MyID")), Equal
ApplyBinaryComparisonOperator (ComparisonColumn CurrentTable (ColumnName "MyAnythingColumn") (ScalarType "MyAnything"))
Equal (ScalarValueComparison $ ScalarValue (Aeson.Object mempty) (ScalarType "MyAnything"))
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyIntColumn"}, _ccColumnType = ScalarType "MyInt"}) ]
(ScalarValueComparison $ ScalarValue (Aeson.Number 42.0) (ScalarType "MyInt")), )
ApplyBinaryComparisonOperator
Equal
(ComparisonColumn {_ccPath = CurrentTable, _ccName = ColumnName {unColumnName = "MyAnythingColumn"}, _ccColumnType = ScalarType "MyAnything"})
(ScalarValueComparison $ ScalarValue (Aeson.Object mempty) (ScalarType "MyAnything"))
],
_qOrderBy = Nothing
},
_qrForeach = Nothing
}
) )
mockAgentGraphqlTest "fails parsing float when expecting int" $ \_testEnv performGraphqlRequest -> do 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 MockRequestResults {..} <- performGraphqlRequest mockConfig headers graphqlRequest
@ -717,18 +700,15 @@ tests _opts = describe "Custom scalar parsing tests" $ do
|] |]
where where
customScalarsTable = customScalarsTable =
[ [ (API.FieldName "MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42), [ [ ("MyIntColumn", API.mkColumnFieldValue $ Aeson.Number 42),
(API.FieldName "MyFloatColumn", API.mkColumnFieldValue $ Aeson.Number 3.14), ("MyFloatColumn", API.mkColumnFieldValue $ Aeson.Number 3.14),
(API.FieldName "MyStringColumn", API.mkColumnFieldValue $ Aeson.String "foo"), ("MyStringColumn", API.mkColumnFieldValue $ Aeson.String "foo"),
(API.FieldName "MyBooleanColumn", API.mkColumnFieldValue $ Aeson.Bool True), ("MyBooleanColumn", API.mkColumnFieldValue $ Aeson.Bool True),
(API.FieldName "MyIDColumn", API.mkColumnFieldValue $ Aeson.String "x"), ("MyIDColumn", API.mkColumnFieldValue $ Aeson.String "x"),
(API.FieldName "MyAnythingColumn", API.mkColumnFieldValue $ Aeson.Object mempty) ("MyAnythingColumn", API.mkColumnFieldValue $ Aeson.Object mempty)
] ]
] ]
myIntTable = 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

View File

@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.DeleteMutationsSpec
) )
where where
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
@ -17,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml import Harness.Yaml
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec import Test.Hspec
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -107,38 +109,38 @@ tests _opts = do
{ API._morAffectedRows = 3, { API._morAffectedRows = 3,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 112), [ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"), ("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
( API.FieldName "deletedRows_Artist", ( "deletedRows_Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden") ("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 113), [ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 113),
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The X Factor"), ("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "The X Factor"),
( API.FieldName "deletedRows_Artist", ( "deletedRows_Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden") ("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 114), [ ("deletedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 114),
(API.FieldName "deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "Virtual XI"), ("deletedRows_Title", API.mkColumnFieldValue $ Aeson.String "Virtual XI"),
( API.FieldName "deletedRows_Artist", ( "deletedRows_Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden") ("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
] ]
] ]
) )
@ -174,66 +176,58 @@ tests _opts = do
|] |]
let expectedRequest = let expectedRequest =
API.MutationRequest emptyMutationRequest
{ API._mrTableRelationships = & API.mrTableRelationships
[ API.TableRelationships .~ [ API.TableRelationships
{ API._trSourceTable = API.TableName ("Album" :| []), { API._trSourceTable = mkTableName "Album",
API._trRelationships = API._trRelationships =
HashMap.fromList HashMap.fromList
[ ( API.RelationshipName "Artist", [ ( API.RelationshipName "Artist",
API.Relationship API.Relationship
{ API._rTargetTable = API.TableName ("Artist" :| []), { API._rTargetTable = mkTableName "Artist",
API._rRelationshipType = API.ObjectRelationship, API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
} }
) )
] ]
} }
], ]
API._mrInsertSchema = [], & API.mrOperations
API._mrOperations = .~ [ API.DeleteOperation $
[ API.DeleteOperation $ API.DeleteMutationOperation
API.DeleteMutationOperation { API._dmoTable = mkTableName "Album",
{ API._dmoTable = API.TableName ("Album" :| []), API._dmoWhere =
API._dmoWhere = Just $
Just $ API.And
API.And [ API.ApplyBinaryComparisonOperator
[ API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 90) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 90) (API.ScalarType "number")), API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.GreaterThan
API.GreaterThan (API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 111) (API.ScalarType "number"))
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 111) (API.ScalarType "number")) ],
], API._dmoReturningFields =
API._dmoReturningFields = mkFieldsMap
HashMap.fromList [ ("deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
[ (API.FieldName "deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")), ("deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
(API.FieldName "deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")), ( "deletedRows_Artist",
( API.FieldName "deletedRows_Artist", API.RelField
API.RelField ( API.RelationshipField
( API.RelationshipField (API.RelationshipName "Artist")
(API.RelationshipName "Artist") ( emptyQuery
API.Query & API.qFields
{ _qFields = ?~ mkFieldsMap
Just $ [ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
HashMap.fromList ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
[ (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
}
)
)
]
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest) _mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
mockAgentGraphqlTest "delete row by pk with delete permissions" $ \_testEnv performGraphqlRequest -> do mockAgentGraphqlTest "delete row by pk with delete permissions" $ \_testEnv performGraphqlRequest -> do
@ -257,14 +251,14 @@ tests _opts = do
{ API._morAffectedRows = 1, { API._morAffectedRows = 1,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 112), [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 112),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"), ("Title", API.mkColumnFieldValue $ Aeson.String "The Number of The Beast"),
( API.FieldName "Artist", ( "Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 90), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 90),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden") ("Name", API.mkColumnFieldValue $ Aeson.String "Iron Maiden")
] ]
] ]
) )
@ -288,67 +282,56 @@ tests _opts = do
|] |]
let expectedRequest = let expectedRequest =
API.MutationRequest emptyMutationRequest
{ API._mrTableRelationships = & API.mrTableRelationships
[ API.TableRelationships .~ [ API.TableRelationships
{ API._trSourceTable = API.TableName ("Album" :| []), { API._trSourceTable = mkTableName "Album",
API._trRelationships = API._trRelationships =
HashMap.fromList HashMap.fromList
[ ( API.RelationshipName "Artist", [ ( API.RelationshipName "Artist",
API.Relationship API.Relationship
{ API._rTargetTable = API.TableName ("Artist" :| []), { API._rTargetTable = mkTableName "Artist",
API._rRelationshipType = API.ObjectRelationship, API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
} }
) )
] ]
} }
], ]
API._mrInsertSchema = [], & API.mrOperations
API._mrOperations = .~ [ API.DeleteOperation $
[ API.DeleteOperation $ API.DeleteMutationOperation
API.DeleteMutationOperation { API._dmoTable = mkTableName "Album",
{ API._dmoTable = API.TableName ("Album" :| []), API._dmoWhere =
API._dmoWhere = Just $
Just $ API.And
API.And [ API.ApplyBinaryComparisonOperator
[ API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 90) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 90) (API.ScalarType "number")), API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 112) (API.ScalarType "number"))
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 112) (API.ScalarType "number")) ],
], API._dmoReturningFields =
API._dmoReturningFields = mkFieldsMap
HashMap.fromList [ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")), ("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")), ( "Artist",
( API.FieldName "Artist", API.RelField
API.RelField ( API.RelationshipField
( API.RelationshipField (API.RelationshipName "Artist")
(API.RelationshipName "Artist") ( emptyQuery
API.Query & API.qFields
{ _qFields = ?~ mkFieldsMap
Just $ [ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
HashMap.fromList ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
[ (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
}
)
)
]
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest) _mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -6,8 +6,8 @@ module Test.DataConnector.MockAgent.ErrorSpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest) import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest)
import Harness.Backend.DataConnector.Mock qualified as Mock import Harness.Backend.DataConnector.Mock qualified as Mock
@ -19,6 +19,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml) import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -92,23 +93,14 @@ tests _opts = describe "Error Protocol Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
HashMap.fromList ]
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"), & API.qLimit ?~ 1
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string") )
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
},
_qrForeach = Nothing
}
) )

View File

@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.InsertMutationsSpec
) )
where where
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
@ -17,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml import Harness.Yaml
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec import Test.Hspec
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -116,26 +118,26 @@ tests _opts = do
{ API._morAffectedRows = 2, { API._morAffectedRows = 2,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9001), [ ("insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9001),
(API.FieldName "insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Super Mega Rock"), ("insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Super Mega Rock"),
( API.FieldName "insertedRows_Artist", ( "insertedRows_Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 2), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Accept") ("Name", API.mkColumnFieldValue $ Aeson.String "Accept")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9002), [ ("insertedRows_AlbumId", API.mkColumnFieldValue $ Aeson.Number 9002),
(API.FieldName "insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Accept This"), ("insertedRows_Title", API.mkColumnFieldValue $ Aeson.String "Accept This"),
( API.FieldName "insertedRows_Artist", ( "insertedRows_Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "ArtistId", API.mkColumnFieldValue $ Aeson.Number 2), [ [ ("ArtistId", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Accept") ("Name", API.mkColumnFieldValue $ Aeson.String "Accept")
] ]
] ]
) )
@ -166,85 +168,75 @@ tests _opts = do
|] |]
let expectedRequest = let expectedRequest =
API.MutationRequest emptyMutationRequest
{ API._mrTableRelationships = & API.mrTableRelationships
[ API.TableRelationships .~ [ API.TableRelationships
{ API._trSourceTable = API.TableName ("Album" :| []), { API._trSourceTable = mkTableName "Album",
API._trRelationships = API._trRelationships =
HashMap.fromList HashMap.fromList
[ ( API.RelationshipName "Artist", [ ( API.RelationshipName "Artist",
API.Relationship API.Relationship
{ API._rTargetTable = API.TableName ("Artist" :| []), { API._rTargetTable = mkTableName "Artist",
API._rRelationshipType = API.ObjectRelationship, API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] API._rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
} }
) )
] ]
} }
], ]
API._mrInsertSchema = & API.mrInsertSchema
[ API.TableInsertSchema .~ [ API.TableInsertSchema
{ API._tisTable = API.TableName ("Album" :| []), { API._tisTable = mkTableName "Album",
API._tisFields = API._tisFields =
HashMap.fromList mkFieldsMap
[ (API.FieldName "AlbumId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "AlbumId") (API.ScalarType "number")), [ ("AlbumId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "AlbumId") (API.ScalarType "number")),
(API.FieldName "ArtistId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "ArtistId") (API.ScalarType "number")), ("ArtistId", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "ArtistId") (API.ScalarType "number")),
(API.FieldName "Title", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "Title") (API.ScalarType "string")) ("Title", API.ColumnInsert $ API.ColumnInsertSchema (API.ColumnName "Title") (API.ScalarType "string"))
] ]
} }
], ]
API._mrOperations = & API.mrOperations
[ API.InsertOperation $ .~ [ API.InsertOperation $
API.InsertMutationOperation API.InsertMutationOperation
{ API._imoTable = API.TableName ("Album" :| []), { API._imoTable = mkTableName "Album",
API._imoRows = API._imoRows =
[ API.RowObject $ [ API.RowObject $
HashMap.fromList mkFieldsMap
[ (API.FieldName "AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9001), [ ("AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9001),
(API.FieldName "ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2), ("ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
(API.FieldName "Title", API.mkColumnInsertFieldValue $ Aeson.String "Super Mega Rock") ("Title", API.mkColumnInsertFieldValue $ Aeson.String "Super Mega Rock")
], ],
API.RowObject $ API.RowObject $
HashMap.fromList mkFieldsMap
[ (API.FieldName "AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9002), [ ("AlbumId", API.mkColumnInsertFieldValue $ Aeson.Number 9002),
(API.FieldName "ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2), ("ArtistId", API.mkColumnInsertFieldValue $ Aeson.Number 2),
(API.FieldName "Title", API.mkColumnInsertFieldValue $ Aeson.String "Accept This") ("Title", API.mkColumnInsertFieldValue $ Aeson.String "Accept This")
] ]
], ],
API._imoPostInsertCheck = API._imoPostInsertCheck =
Just $ Just $
API.ApplyBinaryComparisonOperator API.ApplyBinaryComparisonOperator
API.Equal API.Equal
(API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number") (API.ComparisonColumn API.CurrentTable (API.ColumnName "ArtistId") $ API.ScalarType "number")
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 2) (API.ScalarType "number")), (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 2) (API.ScalarType "number")),
API._imoReturningFields = API._imoReturningFields =
HashMap.fromList mkFieldsMap
[ (API.FieldName "insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")), [ ("insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
(API.FieldName "insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")), ("insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
( API.FieldName "insertedRows_Artist", ( "insertedRows_Artist",
API.RelField API.RelField
( API.RelationshipField ( API.RelationshipField
(API.RelationshipName "Artist") (API.RelationshipName "Artist")
API.Query ( emptyQuery
{ _qFields = & API.qFields
Just $ ?~ mkFieldsMap
HashMap.fromList [ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
[ (API.FieldName "ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"), ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
(API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string") ]
], )
_qAggregates = Nothing, )
_qLimit = Nothing, )
_qOffset = Nothing, ]
_qWhere = Nothing, }
_qOrderBy = Nothing ]
}
)
)
]
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest) _mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -5,6 +5,7 @@ module Test.DataConnector.MockAgent.OrderBySpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE 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.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Language.GraphQL.Draft.Syntax.QQ qualified as G import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -111,25 +113,17 @@ tests _opts = describe "Order By Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
HashMap.fromList ]
[ (API.FieldName "AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"), & API.qLimit ?~ 3
(API.FieldName "Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string") & API.qOrderBy ?~ API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| [])
], )
_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
}
) )
mockAgentGraphqlTest "can order by aggregates" $ \_testEnv performGraphqlRequest -> do mockAgentGraphqlTest "can order by aggregates" $ \_testEnv performGraphqlRequest -> do
@ -162,59 +156,48 @@ tests _opts = describe "Order By Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Artist" :| []), (mkTableName "Artist")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))]
{ _trSourceTable = API.TableName ("Artist" :| []), & API.qLimit ?~ 2
_trRelationships = & API.qOrderBy
HashMap.fromList ?~ API.OrderBy
[ ( API.RelationshipName "Albums", ( HashMap.fromList
API.Relationship [ ( API.RelationshipName "Albums",
{ _rTargetTable = API.TableName ("Album" :| []), API.OrderByRelation Nothing mempty
_rRelationshipType = API.ArrayRelationship, )
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] ]
} )
( NE.fromList
[ API.OrderByElement [API.RelationshipName "Albums"] API.OrderByStarCountAggregate API.Ascending,
API.OrderByElement
[API.RelationshipName "Albums"]
( API.OrderBySingleColumnAggregate $
API.SingleColumnAggregate
(API.SingleColumnAggregateFunction [G.name|max|])
(API.ColumnName "AlbumId")
(API.ScalarType "number")
) )
] API.Ascending
} ]
], )
_qrQuery = )
API.Query & API.qrTableRelationships
{ _qFields = .~ [ API.TableRelationships
Just $ { _trSourceTable = mkTableName "Artist",
HashMap.fromList _trRelationships =
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")) HashMap.fromList
], [ ( API.RelationshipName "Albums",
_qAggregates = Nothing, API.Relationship
_qLimit = Just 2, { _rTargetTable = mkTableName "Album",
_qOffset = Nothing, _rRelationshipType = API.ArrayRelationship,
_qWhere = Nothing, _rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")]
_qOrderBy = }
Just $ )
API.OrderBy ]
( HashMap.fromList }
[ ( API.RelationshipName "Albums", ]
API.OrderByRelation Nothing mempty
)
]
)
( NE.fromList
[ API.OrderByElement [API.RelationshipName "Albums"] API.OrderByStarCountAggregate API.Ascending,
API.OrderByElement
[API.RelationshipName "Albums"]
( API.OrderBySingleColumnAggregate $
API.SingleColumnAggregate
(API.SingleColumnAggregateFunction [G.name|max|])
(API.ColumnName "AlbumId")
(API.ScalarType "number")
)
API.Ascending
]
)
},
_qrForeach = Nothing
}
) )
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse

View File

@ -5,6 +5,7 @@ module Test.DataConnector.MockAgent.QueryRelationshipsSpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
@ -19,6 +20,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml) import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -145,18 +147,18 @@ tests _opts = describe "Object Relationships Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"), [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock (We Salute You)"),
( API.FieldName "Genre", ( "Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock")] [ [("Name", API.mkColumnFieldValue $ Aeson.String "Rock")]
] ]
), ),
( API.FieldName "MediaType", ( "MediaType",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")] [ [("Name", API.mkColumnFieldValue $ Aeson.String "MPEG audio file")]
] ]
) )
] ]
@ -179,83 +181,53 @@ tests _opts = describe "Object Relationships Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Track" :| []), (mkTableName "Track")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields
{ _trSourceTable = API.TableName ("Track" :| []), ?~ mkFieldsMap
_trRelationships = [ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
HashMap.fromList ( "Genre",
[ ( API.RelationshipName "Genre", API.RelField
API.Relationship ( API.RelationshipField
{ _rTargetTable = API.TableName ("Genre" :| []), (API.RelationshipName "Genre")
_rRelationshipType = API.ObjectRelationship, (emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
_rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")] )
} ),
), ( "MediaType",
( API.RelationshipName "MediaType", API.RelField
API.Relationship ( API.RelationshipField
{ _rTargetTable = API.TableName ("MediaType" :| []), (API.RelationshipName "MediaType")
_rRelationshipType = API.ObjectRelationship, (emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
_rColumnMapping = )
HashMap.fromList )
[(API.ColumnName "MediaTypeId", API.ColumnName "MediaTypeId")] ]
} & API.qLimit ?~ 1
) )
] & API.qrTableRelationships
} .~ [ API.TableRelationships
], { _trSourceTable = mkTableName "Track",
_qrQuery = _trRelationships =
API.Query HashMap.fromList
{ _qFields = [ ( API.RelationshipName "Genre",
Just $ API.Relationship
HashMap.fromList { _rTargetTable = mkTableName "Genre",
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"), _rRelationshipType = API.ObjectRelationship,
( API.FieldName "Genre", _rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
API.RelField }
( API.RelationshipField ),
(API.RelationshipName "Genre") ( API.RelationshipName "MediaType",
API.Query API.Relationship
{ _qFields = { _rTargetTable = mkTableName "MediaType",
Just $ _rRelationshipType = API.ObjectRelationship,
HashMap.fromList _rColumnMapping =
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string") HashMap.fromList
], [(API.ColumnName "MediaTypeId", API.ColumnName "MediaTypeId")]
_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 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 = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ ( API.FieldName "Album", [ [ ( "Album",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ ( API.FieldName "Artist", [ [ ( "Artist",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[[(API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Zeca Pagodinho")]] [[("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 let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
@ -306,108 +278,83 @@ tests _opts = describe "Object Relationships Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Track" :| []), (mkTableName "Track")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields
{ _trSourceTable = API.TableName ("Track" :| []), ?~ mkFieldsMap
_trRelationships = [ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
HashMap.fromList ( "Album",
[ ( API.RelationshipName "Album", API.RelField
API.Relationship ( API.RelationshipField
{ _rTargetTable = API.TableName ("Album" :| []), (API.RelationshipName "Album")
_rRelationshipType = API.ObjectRelationship, ( emptyQuery
_rColumnMapping = HashMap.fromList [(API.ColumnName "AlbumId", API.ColumnName "AlbumId")] & API.qFields
} ?~ mkFieldsMap
) [ ( "Artist",
] API.RelField
}, ( API.RelationshipField
API.TableRelationships (API.RelationshipName "Artist")
{ _trSourceTable = API.TableName ("Album" :| []), (emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))])
_trRelationships = )
HashMap.fromList )
[ ( API.RelationshipName "Artist", ]
API.Relationship )
{ _rTargetTable = API.TableName ("Artist" :| []), )
_rRelationshipType = API.ObjectRelationship, )
_rColumnMapping = HashMap.fromList [(API.ColumnName "ArtistId", API.ColumnName "ArtistId")] ]
} & API.qLimit ?~ 1
) & API.qOrderBy
] ?~ API.OrderBy
} ( HashMap.fromList
], [ ( API.RelationshipName "Album",
_qrQuery = API.OrderByRelation
API.Query Nothing
{ _qFields = ( HashMap.fromList
Just $ [ ( API.RelationshipName "Artist",
HashMap.fromList API.OrderByRelation
[ (API.FieldName "Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"), Nothing
( API.FieldName "Album", mempty
API.RelField
( API.RelationshipField
(API.RelationshipName "Album")
API.Query
{ _qFields =
Just $
HashMap.fromList
[ ( API.FieldName "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
}
)
)
],
_qAggregates = Nothing,
_qLimit = Nothing,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
}
)
)
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy =
Just $
API.OrderBy
( HashMap.fromList
[ ( API.RelationshipName "Album",
API.OrderByRelation
Nothing
( HashMap.fromList
[ ( API.RelationshipName "Artist",
API.OrderByRelation
Nothing
mempty
)
]
) )
) ]
] )
) )
( NE.fromList ]
[ API.OrderByElement [API.RelationshipName "Album", API.RelationshipName "Artist"] (API.OrderByColumn (API.ColumnName "Name")) API.Descending, )
API.OrderByElement [] (API.OrderByColumn (API.ColumnName "Name")) API.Ascending ( NE.fromList
] [ API.OrderByElement [API.RelationshipName "Album", API.RelationshipName "Artist"] (API.OrderByColumn (API.ColumnName "Name")) API.Descending,
) 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 mockAgentGraphqlTest "works with an order by that navigates a relationship with table permissions" $ \_testEnv performGraphqlRequest -> do
@ -421,8 +368,8 @@ tests _opts = describe "Object Relationships Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3) [ [ ("EmployeeId", API.mkColumnFieldValue $ Aeson.Number 3)
] ]
] ]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
@ -439,75 +386,63 @@ tests _opts = describe "Object Relationships Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Employee" :| []), (mkTableName "Employee")
_qrTableRelationships = ( emptyQuery
[ API.TableRelationships & API.qFields ?~ mkFieldsMap [("EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")]
{ _trSourceTable = API.TableName ("Customer" :| []), & API.qLimit ?~ 1
_trRelationships = & API.qWhere
HashMap.fromList ?~ API.Exists
[ ( API.RelationshipName "SupportRep", (API.RelatedTable $ API.RelationshipName "SupportRepForCustomers")
API.Relationship ( API.ApplyBinaryComparisonOperator
{ _rTargetTable = API.TableName ("Employee" :| []), API.Equal
_rRelationshipType = API.ObjectRelationship, (API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
_rColumnMapping = HashMap.fromList [(API.ColumnName "SupportRepId", API.ColumnName "EmployeeId")] (API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string"))
} )
) & API.qOrderBy
] ?~ API.OrderBy
}, ( HashMap.fromList
API.TableRelationships [ ( API.RelationshipName "SupportRepForCustomers",
{ _trSourceTable = API.TableName ("Employee" :| []), API.OrderByRelation
_trRelationships = ( Just $
HashMap.fromList API.Exists (API.RelatedTable $ API.RelationshipName "SupportRep") $
[ ( API.RelationshipName "SupportRepForCustomers", API.ApplyBinaryComparisonOperator
API.Relationship API.Equal
{ _rTargetTable = API.TableName ("Customer" :| []), (API.ComparisonColumn API.CurrentTable (API.ColumnName "Country") $ API.ScalarType "string")
_rRelationshipType = API.ArrayRelationship, (API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.ColumnName "Country") $ API.ScalarType "string"))
_rColumnMapping = HashMap.fromList [(API.ColumnName "EmployeeId", API.ColumnName "SupportRepId")] )
} mempty
)
]
}
],
_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
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
( HashMap.fromList
[ ( API.RelationshipName "SupportRepForCustomers",
API.OrderByRelation
( Just $
API.Exists (API.RelatedTable $ API.RelationshipName "SupportRep") $
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"))
)
mempty
)
]
) )
(API.OrderByElement [API.RelationshipName "SupportRepForCustomers"] API.OrderByStarCountAggregate API.Descending :| []) ]
}, )
_qrForeach = Nothing (API.OrderByElement [API.RelationshipName "SupportRepForCustomers"] API.OrderByStarCountAggregate API.Descending :| [])
} )
& 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")]
}
)
]
},
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")]
}
)
]
}
]
) )
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -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.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
@ -24,6 +24,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml, shouldReturnYaml) import Harness.Yaml (shouldBeYaml, shouldReturnYaml)
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (HasCallStack, SpecWith, describe, it, shouldBe) import Test.Hspec (HasCallStack, SpecWith, describe, it, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -211,27 +212,27 @@ tests _opts = do
} }
|] |]
let queryResponse = let queryResponse =
mkRowsResponse mkRowsQueryResponse
[ [ ( API.FieldName "query", [ [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkRowsResponse mkRowsQueryResponse
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You") ("Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
], ],
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 4), [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock") ("Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
] ]
] ]
) )
], ],
[ ( API.FieldName "query", [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkRowsResponse mkRowsQueryResponse
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 2), [ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall") ("Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
], ],
[ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 3), [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild") ("Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
] ]
] ]
) )
@ -264,30 +265,20 @@ tests _opts = do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
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
[ 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"))]
] ]
} )
& 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 mockAgentGraphqlTest "can act as the target of a remote object relationship" $ \testEnv performGraphqlRequest -> do
@ -307,30 +298,30 @@ tests _opts = do
} }
|] |]
let queryResponse = let queryResponse =
mkRowsResponse mkRowsQueryResponse
[ [ ( API.FieldName "query", [ [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkRowsResponse mkRowsQueryResponse
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 3), [ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild") ("Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild")
] ]
] ]
) )
], ],
[ ( API.FieldName "query", [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkRowsResponse mkRowsQueryResponse
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You") ("Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
] ]
] ]
) )
], ],
[ ( API.FieldName "query", [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkRowsResponse mkRowsQueryResponse
[ [ (API.FieldName "AlbumId", API.mkColumnFieldValue $ Aeson.Number 4), [ [ ("AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock") ("Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock")
] ]
] ]
) )
@ -364,31 +355,21 @@ tests _opts = do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
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
[ 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"))]
] ]
} )
& 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 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 = let queryResponse =
mkRowsResponse mkRowsQueryResponse
[ [ ( API.FieldName "query", [ [ ( "query",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
mkQueryResponse mkQueryResponse
[ [ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You") ("nodes_Title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
], ],
[ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 4), [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Let There Be Rock") ("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 $ API.mkRelationshipFieldValue $
mkQueryResponse mkQueryResponse
[ [ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 2), [ [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 2),
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall") ("nodes_Title", API.mkColumnFieldValue $ Aeson.String "Balls to the Wall")
], ],
[ (API.FieldName "nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 3), [ ("nodes_AlbumId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "nodes_Title", API.mkColumnFieldValue $ Aeson.String "Restless and Wild") ("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 _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("nodes_AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("nodes_Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
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
[ 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"))]
] ]
} & 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 :: Fixture.Options -> SpecWith (TestEnvironment, Mock.MockAgentEnvironment)
errorTests opts = do errorTests opts = do
it "creating a remote relationship returns an error when it is unsupported by the target" $ \(testEnv, _) -> do it "creating a remote relationship returns an error when it is unsupported by the target" $ \(testEnv, _) -> do

View File

@ -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)

View File

@ -5,8 +5,8 @@ module Test.DataConnector.MockAgent.TransformedConfigurationSpec (spec) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Control.Lens ((?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse) import Harness.Backend.DataConnector.Mock (AgentRequest (..), MockRequestResults (..), mockAgentGraphqlTest, mockQueryResponse)
import Harness.Backend.DataConnector.Mock qualified as Mock import Harness.Backend.DataConnector.Mock qualified as Mock
@ -18,6 +18,7 @@ import Harness.TestEnvironment (GlobalTestEnvironment, TestEnvironment)
import Harness.Yaml (shouldBeYaml) import Harness.Yaml (shouldBeYaml)
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec (SpecWith, describe, shouldBe) import Test.Hspec (SpecWith, describe, shouldBe)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -113,9 +114,9 @@ tests _opts = describe "Transformed Configuration Tests" $ do
} }
|] |]
let queryResponse = let queryResponse =
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "id", API.mkColumnFieldValue $ Aeson.Number 1), [ [ ("id", API.mkColumnFieldValue $ Aeson.Number 1),
(API.FieldName "title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You") ("title", API.mkColumnFieldValue $ Aeson.String "For Those About To Rock We Salute You")
] ]
] ]
let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse let mockConfig = Mock.chinookMock & mockQueryResponse queryResponse
@ -133,25 +134,16 @@ tests _opts = describe "Transformed Configuration Tests" $ do
_mrrRecordedRequest _mrrRecordedRequest
`shouldBe` Just `shouldBe` Just
( Query $ ( Query $
API.QueryRequest mkQueryRequest
{ _qrTable = API.TableName ("Album" :| []), (mkTableName "Album")
_qrTableRelationships = [], ( emptyQuery
_qrQuery = & API.qFields
API.Query ?~ mkFieldsMap
{ _qFields = [ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
Just $ ("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
HashMap.fromList ]
[ (API.FieldName "id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"), & API.qLimit ?~ 1
(API.FieldName "title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string") )
],
_qAggregates = Nothing,
_qLimit = Just 1,
_qOffset = Nothing,
_qWhere = Nothing,
_qOrderBy = Nothing
},
_qrForeach = Nothing
}
) )
Aeson.toJSON _mrrRecordedRequestConfig Aeson.toJSON _mrrRecordedRequestConfig
@ -161,6 +153,3 @@ tests _opts = describe "Transformed Configuration Tests" $ do
env: "bar env default" env: "bar env default"
session: "foo session default" session: "foo session default"
|] |]
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -3,6 +3,7 @@ module Test.DataConnector.MockAgent.UpdateMutationsSpec
) )
where where
import Control.Lens ((.~), (?~))
import Data.Aeson qualified as Aeson import Data.Aeson qualified as Aeson
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.HashMap.Strict qualified as HashMap import Data.HashMap.Strict qualified as HashMap
@ -18,6 +19,7 @@ import Harness.Yaml
import Hasura.Backends.DataConnector.API qualified as API import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Prelude import Hasura.Prelude
import Language.GraphQL.Draft.Syntax.QQ qualified as G import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.DataConnector.MockAgent.TestHelpers
import Test.Hspec import Test.Hspec
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -127,35 +129,35 @@ tests _opts = do
{ API._morAffectedRows = 3, { API._morAffectedRows = 3,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
@ -188,91 +190,78 @@ tests _opts = do
|] |]
let expectedRequest = let expectedRequest =
API.MutationRequest emptyMutationRequest
{ API._mrTableRelationships = & API.mrTableRelationships
[ API.TableRelationships .~ [ API.TableRelationships
{ API._trSourceTable = API.TableName ("Track" :| []), { API._trSourceTable = mkTableName "Track",
API._trRelationships = API._trRelationships =
HashMap.fromList HashMap.fromList
[ ( API.RelationshipName "Genre", [ ( API.RelationshipName "Genre",
API.Relationship API.Relationship
{ API._rTargetTable = API.TableName ("Genre" :| []), { API._rTargetTable = mkTableName "Genre",
API._rRelationshipType = API.ObjectRelationship, API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")] API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
} }
) )
] ]
} }
], ]
API._mrInsertSchema = [], & API.mrOperations
API._mrOperations = .~ [ API.UpdateOperation $
[ API.UpdateOperation $ API.UpdateMutationOperation
API.UpdateMutationOperation { API._umoTable = mkTableName "Track",
{ API._umoTable = API.TableName ("Track" :| []), API._umoUpdates =
API._umoUpdates = [ API.SetColumn $
[ API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "Name",
{ API._rcovColumn = API.ColumnName "Name", API._rcovValue = Aeson.String "Another Name",
API._rcovValue = Aeson.String "Another Name", API._rcovValueType = API.ScalarType "string"
API._rcovValueType = API.ScalarType "string" },
}, API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "Milliseconds",
{ API._rcovColumn = API.ColumnName "Milliseconds", API._rcovValue = Aeson.Number 1000,
API._rcovValue = Aeson.Number 1000, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" },
}, API.SetColumn $
API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "AlbumId",
{ API._rcovColumn = API.ColumnName "AlbumId", API._rcovValue = Aeson.Number 3,
API._rcovValue = Aeson.Number 3, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" }
} ],
], API._umoWhere =
API._umoWhere = Just $
Just $ API.And
API.And [ API.ApplyBinaryComparisonOperator
[ API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")), API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "GenreId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "GenreId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 1) (API.ScalarType "number"))
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 1) (API.ScalarType "number")) ],
], API._umoPostUpdateCheck =
API._umoPostUpdateCheck = Just $
Just $ API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.GreaterThan
API.GreaterThan (API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number")), API._umoReturningFields =
API._umoReturningFields = mkFieldsMap
HashMap.fromList [ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")), ("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")), ( "updatedRows_Genre",
( API.FieldName "updatedRows_Genre", API.RelField
API.RelField ( API.RelationshipField
( API.RelationshipField (API.RelationshipName "Genre")
(API.RelationshipName "Genre") (emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
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
}
)
)
]
}
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest) _mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
mockAgentGraphqlTest "update_many rows with update permissions" $ \_testEnv performGraphqlRequest -> do mockAgentGraphqlTest "update_many rows with update permissions" $ \_testEnv performGraphqlRequest -> do
@ -301,13 +290,13 @@ tests _opts = do
{ API._morAffectedRows = 1, { API._morAffectedRows = 1,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 3),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Another Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
@ -318,24 +307,24 @@ tests _opts = do
{ API._morAffectedRows = 2, { API._morAffectedRows = 2,
API._morReturning = API._morReturning =
Just Just
[ HashMap.fromList [ mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 4),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
], ],
HashMap.fromList mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5), [ ("updatedRows_TrackId", API.mkColumnFieldValue $ Aeson.Number 5),
(API.FieldName "updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"), ("updatedRows_Name", API.mkColumnFieldValue $ Aeson.String "Better Name"),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.mkRelationshipFieldValue $ API.mkRelationshipFieldValue $
rowsResponse mkRowsQueryResponse
[ [ (API.FieldName "Name", API.mkColumnFieldValue $ Aeson.String "Rock") [ [ ("Name", API.mkColumnFieldValue $ Aeson.String "Rock")
] ]
] ]
) )
@ -376,126 +365,110 @@ tests _opts = do
(API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number") (API.ComparisonColumn API.CurrentTable (API.ColumnName "UnitPrice") $ API.ScalarType "number")
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number")) (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 0) (API.ScalarType "number"))
let sharedReturning = let sharedReturning =
HashMap.fromList mkFieldsMap
[ (API.FieldName "updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")), [ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
(API.FieldName "updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")), ("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
( API.FieldName "updatedRows_Genre", ( "updatedRows_Genre",
API.RelField API.RelField
( API.RelationshipField ( API.RelationshipField
(API.RelationshipName "Genre") (API.RelationshipName "Genre")
API.Query (emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
{ _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
}
) )
) )
] ]
let expectedRequest = let expectedRequest =
API.MutationRequest emptyMutationRequest
{ API._mrTableRelationships = & API.mrTableRelationships
[ API.TableRelationships .~ [ API.TableRelationships
{ API._trSourceTable = API.TableName ("Track" :| []), { API._trSourceTable = mkTableName "Track",
API._trRelationships = API._trRelationships =
HashMap.fromList HashMap.fromList
[ ( API.RelationshipName "Genre", [ ( API.RelationshipName "Genre",
API.Relationship API.Relationship
{ API._rTargetTable = API.TableName ("Genre" :| []), { API._rTargetTable = mkTableName "Genre",
API._rRelationshipType = API.ObjectRelationship, API._rRelationshipType = API.ObjectRelationship,
API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")] API._rColumnMapping = HashMap.fromList [(API.ColumnName "GenreId", API.ColumnName "GenreId")]
} }
) )
] ]
} }
], ]
API._mrInsertSchema = [], & API.mrOperations
API._mrOperations = .~ [ API.UpdateOperation $
[ API.UpdateOperation $ API.UpdateMutationOperation
API.UpdateMutationOperation { API._umoTable = mkTableName "Track",
{ API._umoTable = API.TableName ("Track" :| []), API._umoUpdates =
API._umoUpdates = [ API.SetColumn $
[ API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "Name",
{ API._rcovColumn = API.ColumnName "Name", API._rcovValue = Aeson.String "Another Name",
API._rcovValue = Aeson.String "Another Name", API._rcovValueType = API.ScalarType "string"
API._rcovValueType = API.ScalarType "string" },
}, API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "Milliseconds",
{ API._rcovColumn = API.ColumnName "Milliseconds", API._rcovValue = Aeson.Number 1000,
API._rcovValue = Aeson.Number 1000, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" },
}, API.SetColumn $
API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "AlbumId",
{ API._rcovColumn = API.ColumnName "AlbumId", API._rcovValue = Aeson.Number 3,
API._rcovValue = Aeson.Number 3, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" }
} ],
], API._umoWhere =
API._umoWhere = Just $
Just $ API.And
API.And [ API.ApplyBinaryComparisonOperator
[ API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")), API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number"))
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")) ],
], API._umoPostUpdateCheck = sharedPostUpdateCheck,
API._umoPostUpdateCheck = sharedPostUpdateCheck, API._umoReturningFields = sharedReturning
API._umoReturningFields = sharedReturning },
}, API.UpdateOperation $
API.UpdateOperation $ API.UpdateMutationOperation
API.UpdateMutationOperation { API._umoTable = mkTableName "Track",
{ API._umoTable = API.TableName ("Track" :| []), API._umoUpdates =
API._umoUpdates = [ API.SetColumn $
[ API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "Name",
{ API._rcovColumn = API.ColumnName "Name", API._rcovValue = Aeson.String "Better Name",
API._rcovValue = Aeson.String "Better Name", API._rcovValueType = API.ScalarType "string"
API._rcovValueType = API.ScalarType "string" },
}, API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $
API.CustomUpdateColumnOperator (API.UpdateColumnOperatorName [G.name|inc|]) $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "UnitPrice",
{ API._rcovColumn = API.ColumnName "UnitPrice", API._rcovValue = Aeson.Number 1,
API._rcovValue = Aeson.Number 1, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" },
}, API.SetColumn $
API.SetColumn $ API.RowColumnOperatorValue
API.RowColumnOperatorValue { API._rcovColumn = API.ColumnName "AlbumId",
{ API._rcovColumn = API.ColumnName "AlbumId", API._rcovValue = Aeson.Number 3,
API._rcovValue = Aeson.Number 3, API._rcovValueType = API.ScalarType "number"
API._rcovValueType = API.ScalarType "number" }
} ],
], API._umoWhere =
API._umoWhere = Just $
Just $ API.And
API.And [ API.ApplyBinaryComparisonOperator
[ API.ApplyBinaryComparisonOperator API.Equal
API.Equal (API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "AlbumId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")),
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")), API.ApplyBinaryComparisonOperator
API.ApplyBinaryComparisonOperator API.GreaterThan
API.GreaterThan (API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number")
(API.ComparisonColumn API.CurrentTable (API.ColumnName "TrackId") $ API.ScalarType "number") (API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number"))
(API.ScalarValueComparison $ API.ScalarValue (Aeson.Number 3) (API.ScalarType "number")) ],
], API._umoPostUpdateCheck = sharedPostUpdateCheck,
API._umoPostUpdateCheck = sharedPostUpdateCheck, API._umoReturningFields = sharedReturning
API._umoReturningFields = sharedReturning }
} ]
]
}
_mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest) _mrrRecordedRequest `shouldBe` Just (Mutation expectedRequest)
rowsResponse :: [[(API.FieldName, API.FieldValue)]] -> API.QueryResponse
rowsResponse rows = API.QueryResponse (Just $ HashMap.fromList <$> rows) Nothing

View File

@ -11,6 +11,7 @@ module Hasura.Backends.DataConnector.API.V0.Query
Query (..), Query (..),
qFields, qFields,
qAggregates, qAggregates,
qAggregatesLimit,
qLimit, qLimit,
qOffset, qOffset,
qWhere, qWhere,
@ -92,9 +93,14 @@ data Query = Query
_qFields :: Maybe (HashMap FieldName Field), _qFields :: Maybe (HashMap FieldName Field),
-- | Map of aggregate field name to Aggregate definition -- | Map of aggregate field name to Aggregate definition
_qAggregates :: Maybe (HashMap FieldName API.V0.Aggregate), _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, _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, _qOffset :: Maybe Int,
-- | Optionally constrain the results to satisfy some predicate. -- | Optionally constrain the results to satisfy some predicate.
_qWhere :: Maybe API.V0.Expression, _qWhere :: Maybe API.V0.Expression,
@ -112,9 +118,11 @@ instance HasCodec Query where
.= _qFields .= _qFields
<*> optionalFieldOrNull "aggregates" "Aggregate fields of the query" <*> optionalFieldOrNull "aggregates" "Aggregate fields of the query"
.= _qAggregates .= _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 .= _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 .= _qOffset
<*> optionalFieldOrNull "where" "Optionally constrain the results to satisfy some predicate" <*> optionalFieldOrNull "where" "Optionally constrain the results to satisfy some predicate"
.= _qWhere .= _qWhere

View File

@ -598,7 +598,7 @@ findColumnScalarType API.SchemaResponse {..} tableName columnName =
columnInfo = find (\API.ColumnInfo {..} -> _ciName == columnName) =<< API._tiColumns <$> tableInfo columnInfo = find (\API.ColumnInfo {..} -> _ciName == columnName) =<< API._tiColumns <$> tableInfo
emptyQuery :: API.Query 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
emptyMutationRequest = API.MutationRequest mempty mempty mempty emptyMutationRequest = API.MutationRequest mempty mempty mempty

View File

@ -52,12 +52,24 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
it "counts all rows, after applying pagination" $ do it "counts all rows, after applying pagination" $ do
let offset = 400 let offset = 400
let limit = 20 let aggregatesLimit = 20
let aggregates = Data.mkFieldsMap [("count_all", StarCount)] 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 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)] let expectedAggregates = Data.mkFieldsMap [("count_all", Number $ fromIntegral invoiceCount)]
Data.responseAggregates response `jsonShouldBe` expectedAggregates Data.responseAggregates response `jsonShouldBe` expectedAggregates
@ -76,16 +88,16 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseRows response `rowsShouldBe` [] Data.responseRows response `rowsShouldBe` []
it "can count all rows with non-null values in a column, after applying pagination and filtering" $ do 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 where' = ApplyBinaryComparisonOperator GreaterThanOrEqual (_tdCurrentComparisonColumn "InvoiceId" invoiceIdScalarType) (Data.scalarValueComparison (Number 380) invoiceIdScalarType)
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") False)] 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 response <- queryGuarded queryRequest
let invoiceCount = let invoiceCount =
_tdInvoicesRows _tdInvoicesRows
& filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380)) & filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380))
& take limit & take aggregatesLimit
& mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString)) & mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString))
& length & length
@ -106,18 +118,18 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseRows response `rowsShouldBe` [] Data.responseRows response `rowsShouldBe` []
it "can count all rows with distinct non-null values in a column, after applying pagination and filtering" $ do 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) 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 -- 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 orderBy = OrderBy mempty $ _tdOrderByColumn [] "InvoiceId" Ascending :| []
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") True)] 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 response <- queryGuarded queryRequest
let billingStateCount = let billingStateCount =
_tdInvoicesRows _tdInvoicesRows
& filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380)) & filter ((^? Data.field "InvoiceId" . Data._ColumnFieldNumber) >>> (>= Just 380))
& take limit & take aggregatesLimit
& mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString)) & mapMaybe ((^? Data.field "BillingState" . Data._ColumnFieldString))
& HashSet.fromList & HashSet.fromList
& length & length
@ -127,6 +139,22 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseAggregates response `jsonShouldBe` expectedAggregates Data.responseAggregates response `jsonShouldBe` expectedAggregates
Data.responseRows response `rowsShouldBe` [] 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 describe "Single Column Function" $ do
it "can get the max total from all rows" $ do it "can get the max total from all rows" $ do
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)] let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)]
@ -140,18 +168,18 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseRows response `rowsShouldBe` [] Data.responseRows response `rowsShouldBe` []
it "can get the max total from all rows, after applying pagination, filtering and ordering" $ do 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 where' = ApplyBinaryComparisonOperator Equal (_tdCurrentComparisonColumn "BillingCountry" billingCountryScalarType) (Data.scalarValueComparison (String "USA") billingCountryScalarType)
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingPostalCode" Descending :| [_tdOrderByColumn [] "InvoiceId" Ascending] let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingPostalCode" Descending :| [_tdOrderByColumn [] "InvoiceId" Ascending]
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)] 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 response <- queryGuarded queryRequest
let maxTotal = let maxTotal =
_tdInvoicesRows _tdInvoicesRows
& filter ((^? Data.field "BillingCountry" . Data._ColumnFieldString) >>> (== Just "USA")) & filter ((^? Data.field "BillingCountry" . Data._ColumnFieldString) >>> (== Just "USA"))
& sortOn (Down . (^? Data.field "BillingPostalCode")) & sortOn (Down . (^? Data.field "BillingPostalCode"))
& take limit & take aggregatesLimit
& mapMaybe ((^? Data.field "Total" . Data._ColumnFieldNumber)) & mapMaybe ((^? Data.field "Total" . Data._ColumnFieldNumber))
& maximum & maximum
@ -190,6 +218,22 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseAggregates response `jsonShouldBe` expectedAggregates Data.responseAggregates response `jsonShouldBe` expectedAggregates
Data.responseRows response `rowsShouldBe` [] 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 describe "Multiple Aggregates and Returning Rows" $ do
it "can get the max total from all rows, the count and the distinct count, simultaneously" $ do it "can get the max total from all rows, the count and the distinct count, simultaneously" $ do
let aggregates = 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 where' = ApplyBinaryComparisonOperator Equal (_tdCurrentComparisonColumn "BillingCountry" billingCountryScalarType) (Data.scalarValueComparison (String "Canada") billingCountryScalarType)
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingAddress" Ascending :| [_tdOrderByColumn [] "InvoiceId" Ascending] let orderBy = OrderBy mempty $ _tdOrderByColumn [] "BillingAddress" Ascending :| [_tdOrderByColumn [] "InvoiceId" Ascending]
let aggregates = Data.mkFieldsMap [("min", singleColumnAggregateMin (_tdColumnName "Total") invoiceTotalScalarType)] 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 response <- queryGuarded queryRequest
let invoiceRows = let invoiceRows =
@ -266,6 +310,56 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
Data.responseRows response `rowsShouldBe` expectedRows Data.responseRows response `rowsShouldBe` expectedRows
Data.responseAggregates response `jsonShouldBe` expectedAggregates 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) $ when (isJust relationshipCapabilities) $
describe "Aggregates via Relationships" $ do describe "Aggregates via Relationships" $ do
it "can query aggregates via an array relationship" $ 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 describe "Aggregates over ordered and paginated tables" $ do
it "orders by a column" $ do it "orders by a column" $ do
let offset = 2 let offset = 2
let limit = 5 let aggregatesLimit = 5
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Descending :| [] let orderBy = OrderBy mempty $ _tdOrderByColumn [] "Title" Ascending :| []
let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Title") albumTitleScalarType)] let aggregates = Data.mkFieldsMap [("max", singleColumnAggregateMax (_tdColumnName "Title") albumTitleScalarType)]
let queryRequest = let queryRequest =
albumsQueryRequest albumsQueryRequest
@ -396,15 +490,15 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
%~ ( qAggregates ?~ aggregates %~ ( qAggregates ?~ aggregates
>>> qOrderBy ?~ orderBy >>> qOrderBy ?~ orderBy
>>> qOffset ?~ offset >>> qOffset ?~ offset
>>> qLimit ?~ limit >>> qAggregatesLimit ?~ aggregatesLimit
) )
response <- queryGuarded queryRequest response <- queryGuarded queryRequest
let names = let names =
_tdAlbumsRows _tdAlbumsRows
& sortOn (Down . (^? Data.field "Title")) & sortOn ((^? Data.field "Title"))
& drop offset & drop offset
& take limit & take aggregatesLimit
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString) & mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)] 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 when (isJust relationshipCapabilities) . describe "involving related tables in the ordering" $ do
it "orders by a column" $ do it "orders by a column" $ do
let offset = 10 let offset = 10
let limit = 50 let aggregatesLimit = 50
let orderByRelations = HashMap.fromList [(_tdArtistRelationshipName, OrderByRelation Nothing mempty)] let orderByRelations = HashMap.fromList [(_tdArtistRelationshipName, OrderByRelation Nothing mempty)]
let orderBy = let orderBy =
OrderBy orderByRelations $ OrderBy orderByRelations $
@ -431,7 +525,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
%~ ( qAggregates ?~ aggregates %~ ( qAggregates ?~ aggregates
>>> qOrderBy ?~ orderBy >>> qOrderBy ?~ orderBy
>>> qOffset ?~ offset >>> qOffset ?~ offset
>>> qLimit ?~ limit >>> qAggregatesLimit ?~ aggregatesLimit
) )
response <- queryGuarded queryRequest response <- queryGuarded queryRequest
@ -442,7 +536,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
_tdAlbumsRows _tdAlbumsRows
& sortOn (\album -> getRelatedArtist album ^? _Just . Data.field "Name") & sortOn (\album -> getRelatedArtist album ^? _Just . Data.field "Name")
& drop offset & drop offset
& take limit & take aggregatesLimit
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString) & mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)] 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 it "orders by an aggregate" $ do
let offset = 15 let offset = 15
let limit = 10 let aggregatesLimit = 10
let orderByRelations = HashMap.fromList [(_tdTracksRelationshipName, OrderByRelation Nothing mempty)] let orderByRelations = HashMap.fromList [(_tdTracksRelationshipName, OrderByRelation Nothing mempty)]
let orderBy = let orderBy =
OrderBy orderByRelations $ OrderBy orderByRelations $
@ -468,7 +562,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
%~ ( qAggregates ?~ aggregates %~ ( qAggregates ?~ aggregates
>>> qOrderBy ?~ orderBy >>> qOrderBy ?~ orderBy
>>> qOffset ?~ offset >>> qOffset ?~ offset
>>> qLimit ?~ limit >>> qAggregatesLimit ?~ aggregatesLimit
) )
response <- queryGuarded queryRequest response <- queryGuarded queryRequest
@ -480,7 +574,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
_tdAlbumsRows _tdAlbumsRows
& sortOn (\album -> (Down $ getRelatedTracksCount album, Down $ album ^? Data.field "Title" . Data._ColumnFieldString)) & sortOn (\album -> (Down $ getRelatedTracksCount album, Down $ album ^? Data.field "Title" . Data._ColumnFieldString))
& drop offset & drop offset
& take limit & take aggregatesLimit
& mapMaybe (^? Data.field "Title" . Data._ColumnFieldString) & mapMaybe (^? Data.field "Title" . Data._ColumnFieldString)
let expectedAggregates = Data.mkFieldsMap [("max", aggregate (String . maximum) names)] 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)] tracksAggregates = Data.mkFieldsMap [("aggregate_count", StarCount)]
tracksWhere = ApplyBinaryComparisonOperator LessThan (_tdCurrentComparisonColumn "Milliseconds" millisecondsScalarType) (Data.scalarValueComparison (Number 300000) millisecondsScalarType) tracksWhere = ApplyBinaryComparisonOperator LessThan (_tdCurrentComparisonColumn "Milliseconds" millisecondsScalarType) (Data.scalarValueComparison (Number 300000) millisecondsScalarType)
tracksOrderBy = OrderBy mempty $ _tdOrderByColumn [] "Name" Descending :| [] 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 = albumsFields =
Data.mkFieldsMap Data.mkFieldsMap
[ ("nodes_Title", _tdColumnField _tdAlbumsTableName "Title"), [ ("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) ApplyBinaryComparisonOperator LessThan (_tdCurrentComparisonColumn "Name" artistNameScalarType) (Data.scalarValueComparison (String "B") artistNameScalarType)
] ]
artistOrderBy = OrderBy mempty $ _tdOrderByColumn [] "Name" Descending :| [] 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 in QueryRequest
_tdArtistsTableName _tdArtistsTableName
[ Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships, [ Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships,

View File

@ -146,6 +146,7 @@ translateAnnSelect sessionVariables translateFieldsAndAggregates tableName selec
API.Query API.Query
{ _qFields = mapFieldNameHashMap <$> _faaFields, { _qFields = mapFieldNameHashMap <$> _faaFields,
_qAggregates = mapFieldNameHashMap <$> _faaAggregates, _qAggregates = mapFieldNameHashMap <$> _faaAggregates,
_qAggregatesLimit = _saLimit (_asnArgs selectG) <* _faaAggregates, -- Only include the aggregates limit if we actually have aggregrates
_qLimit = _qLimit =
fmap getMin $ fmap getMin $
foldMap foldMap
@ -310,6 +311,7 @@ translateAnnField sessionVariables sourceTableName = \case
{ _qFields = Just $ mapFieldNameHashMap fields, { _qFields = Just $ mapFieldNameHashMap fields,
_qAggregates = mempty, _qAggregates = mempty,
_qWhere = whereClause, _qWhere = whereClause,
_qAggregatesLimit = Nothing,
_qLimit = Nothing, _qLimit = Nothing,
_qOffset = Nothing, _qOffset = Nothing,
_qOrderBy = Nothing _qOrderBy = Nothing

View File

@ -89,6 +89,7 @@ mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema ar
API.Query API.Query
{ _qFields = Just $ mapFieldNameHashMap fields, { _qFields = Just $ mapFieldNameHashMap fields,
_qAggregates = Nothing, _qAggregates = Nothing,
_qAggregatesLimit = Nothing,
_qLimit = Nothing, _qLimit = Nothing,
_qOffset = Nothing, _qOffset = Nothing,
_qWhere = whereClause, _qWhere = whereClause,

View File

@ -41,7 +41,7 @@ spec = do
} }
|] |]
describe "RelationshipField" $ 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 testToFromJSONToSchema
(RelField $ RelationshipField (RelationshipName "a_relationship") query) (RelField $ RelationshipField (RelationshipName "a_relationship") query)
[aesonQQ| [aesonQQ|
@ -57,6 +57,7 @@ spec = do
Query Query
{ _qFields = Just $ HashMap.fromList [(FieldName "my_field_alias", ColumnField (ColumnName "my_field_name") (ScalarType "string"))], { _qFields = Just $ HashMap.fromList [(FieldName "my_field_alias", ColumnField (ColumnName "my_field_name") (ScalarType "string"))],
_qAggregates = Just $ HashMap.fromList [(FieldName "my_aggregate", StarCount)], _qAggregates = Just $ HashMap.fromList [(FieldName "my_aggregate", StarCount)],
_qAggregatesLimit = Just 5,
_qLimit = Just 10, _qLimit = Just 10,
_qOffset = Just 20, _qOffset = Just 20,
_qWhere = Just $ And [], _qWhere = Just $ And [],
@ -67,6 +68,7 @@ spec = do
[aesonQQ| [aesonQQ|
{ "fields": {"my_field_alias": {"type": "column", "column": "my_field_name", "column_type": "string"}}, { "fields": {"my_field_alias": {"type": "column", "column": "my_field_name", "column_type": "string"}},
"aggregates": { "my_aggregate": { "type": "star_count" } }, "aggregates": { "my_aggregate": { "type": "star_count" } },
"aggregates_limit": 5,
"limit": 10, "limit": 10,
"offset": 20, "offset": 20,
"where": {"type": "and", "expressions": []}, "where": {"type": "and", "expressions": []},
@ -91,7 +93,7 @@ spec = do
QueryRequest QueryRequest
{ _qrTable = TableName ["my_table"], { _qrTable = TableName ["my_table"],
_qrTableRelationships = [], _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"))] :| []) _qrForeach = Just (HashMap.fromList [(ColumnName "my_id", ScalarValue (J.Number 666) (ScalarType "number"))] :| [])
} }
testToFromJSONToSchema testToFromJSONToSchema
@ -181,6 +183,7 @@ genQuery =
<*> Gen.maybe (genFieldMap genAggregate) <*> Gen.maybe (genFieldMap genAggregate)
<*> Gen.maybe (Gen.int defaultRange) <*> Gen.maybe (Gen.int defaultRange)
<*> Gen.maybe (Gen.int defaultRange) <*> Gen.maybe (Gen.int defaultRange)
<*> Gen.maybe (Gen.int defaultRange)
<*> Gen.maybe genExpression <*> Gen.maybe genExpression
<*> Gen.maybe genOrderBy <*> Gen.maybe genOrderBy